#!/bin/bash
{
	#////////////////////////////////////
	# DietPi Software
	#
	#////////////////////////////////////
	# Created by Daniel Knight / daniel.knight@dietpi.com / dietpi.com
	#
	#////////////////////////////////////
	#
	# Info:
	# - Location: /boot/dietpi/dietpi-software
	# - Installs "ready to run" software with optimisations unique to the device.
	# - Generates and uses /boot/dietpi/.installed (software list) # -1=selected for uninstall, 0=not installed, 1=selected for install, 2=installed
	USAGE='
Usage: dietpi-software [<command> [<software_id>...]]
Available commands:
  <empty>			Interactive menu to install or uninstall software
  install <software_id>...	Install each software given by space-separated list of IDs
  reinstall <software_id>...	Reinstall each software given by space-separated list of IDs
  uninstall <software_id>...	Uninstall each software given by space-separated list of IDs
  list				Print a list with IDs and info for all available software titles
  free				Print an unused software ID, free for a new software implementation
'	#////////////////////////////////////

	# Import DietPi-Globals ---------------------------------------------------------------
	. /boot/dietpi/func/dietpi-globals
	readonly G_PROGRAM_NAME='DietPi-Software'
	G_CHECK_ROOT_USER
	G_CHECK_ROOTFS_RW
	G_INIT
	# Import DietPi-Globals ---------------------------------------------------------------

	#/////////////////////////////////////////////////////////////////////////////////////
	# Filepath
	#/////////////////////////////////////////////////////////////////////////////////////
	readonly FP_INSTALLED_FILE='/boot/dietpi/.installed'

	# Used to set user/personal data directories (e.g.: usbdrive)
	FP_DIETPI_DEDICATED_USBDRIVE=

	Write_InstallFileList(){

		> $FP_INSTALLED_FILE

		# Save installed states
		for i in "${!aSOFTWARE_NAME[@]}"
		do

			# Never save pending state for software (=1).
			if (( ${aSOFTWARE_INSTALL_STATE[$i]} == 1 )); then

				echo "aSOFTWARE_INSTALL_STATE[$i]=0" >> $FP_INSTALLED_FILE

			else

				echo "aSOFTWARE_INSTALL_STATE[$i]=${aSOFTWARE_INSTALL_STATE[$i]}" >> $FP_INSTALLED_FILE

			fi

		done

		# Misc
		cat << _EOF_ >> $FP_INSTALLED_FILE

# DietPi Choice System: SSH Server
INDEX_SSHSERVER_CURRENT=$INDEX_SSHSERVER_CURRENT
INDEX_SSHSERVER_TARGET=$INDEX_SSHSERVER_TARGET

# DietPi Choice System: File Server
INDEX_FILESERVER_CURRENT=$INDEX_FILESERVER_CURRENT
INDEX_FILESERVER_TARGET=$INDEX_FILESERVER_TARGET

# DietPi Choice System: Logging
INDEX_LOGGING_CURRENT=$INDEX_LOGGING_CURRENT
INDEX_LOGGING_TARGET=$INDEX_LOGGING_TARGET

# DietPi Preference System: Webserver base
INDEX_WEBSERVER_CURRENT=$INDEX_WEBSERVER_CURRENT
INDEX_WEBSERVER_TARGET=$INDEX_WEBSERVER_TARGET
_EOF_

	}

	Read_InstallFileList(){

		# Load Software states
		G_DIETPI-NOTIFY -2 'Reading database'

		# Load
		[[ -f $FP_INSTALLED_FILE ]] && . $FP_INSTALLED_FILE

		# Always reset choice system during first run to defaults: https://github.com/MichaIng/DietPi/issues/1122
		if (( $G_DIETPI_INSTALL_STAGE == 1 )); then

			INDEX_SSHSERVER_CURRENT=-1
			INDEX_SSHSERVER_TARGET=-1

			INDEX_FILESERVER_CURRENT=0
			INDEX_FILESERVER_TARGET=0

			INDEX_LOGGING_CURRENT=-1
			INDEX_LOGGING_TARGET=-1

			INDEX_WEBSERVER_CURRENT=-2
			INDEX_WEBSERVER_TARGET=-2

		fi

		G_DIETPI-NOTIFY 0 'Reading database'

	}

	Check_Internet_and_NTPD(){

		# Checking network connectivity
		# shellcheck disable=SC2119
		G_CHECK_CON

		# Checking DNS resolver
		# shellcheck disable=SC2119
		G_CHECK_DNS

		# Network time sync
		/boot/dietpi/func/run_ntpd

	}

	#/////////////////////////////////////////////////////////////////////////////////////
	# Installation System
	#/////////////////////////////////////////////////////////////////////////////////////
	# Since no automated reboot is done anymore after installs, collect services to start manually, when not controlled by DietPi-Services
	aSTART_SERVICES=()

	# Uninstall flag, used in software installations + removals, runs Uninstall_Software()
	UNINSTALL_REQUIRED=0

	# Global password for software installs
	GLOBAL_PW=
	Update_Global_Pw()
	{
		# Use new "pbkdf2" function on Buster to resolve warning about deprecated key derivation used: https://github.com/MichaIng/DietPi/issues/2213
		local pbkdf2=() encrypt=0
		(( $G_DISTRO > 4 )) && pbkdf2=('-iter' '10000')

		# Read encrypted password
		if [[ -f '/var/lib/dietpi/dietpi-software/.GLOBAL_PW.bin' ]]
		then
			if ! GLOBAL_PW=$(openssl enc -d -a -md sha256 -aes-256-cbc "${pbkdf2[@]}" -salt -pass pass:'DietPiRocks!' -in /var/lib/dietpi/dietpi-software/.GLOBAL_PW.bin)
			then
				# Allow decryption without "-iter 10000" and re-encrypt on dist-upgraded Buster systems
				encrypt=1
				# In case of error, assure empty password to fallback to default
				(( $G_DISTRO > 4 )) && GLOBAL_PW=$(openssl enc -d -a -md sha256 -aes-256-cbc -salt -pass pass:'DietPiRocks!' -in /var/lib/dietpi/dietpi-software/.GLOBAL_PW.bin) || GLOBAL_PW=
			fi

		# If encryption has not yet been done, do it now!
		else
			encrypt=1
			GLOBAL_PW=$(sed -n '/^[[:blank:]]*AUTO_SETUP_GLOBAL_PASSWORD=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
			G_EXEC sed -i '/^[[:blank:]]*AUTO_SETUP_GLOBAL_PASSWORD=/c\#AUTO_SETUP_GLOBAL_PASSWORD= # Password has been ecrypted and saved to rootfs' /boot/dietpi.txt
		fi

		# Fallback
		if [[ ! $GLOBAL_PW ]]
		then
			encrypt=1
			GLOBAL_PW='dietpi'
			G_WHIP_MSG "[FAILED] Unable to obtain your global software password
\nThe following fallback password will be used:\n - $GLOBAL_PW
\nYou can change it via:\n - dietpi-config > Security Options > Change Passwords"
		fi

		# Encrypt
		[[ $encrypt == 1 ]] && openssl enc -e -a -md sha256 -aes-256-cbc "${pbkdf2[@]}" -salt -pass pass:'DietPiRocks!' -out /var/lib/dietpi/dietpi-software/.GLOBAL_PW.bin <<< $GLOBAL_PW

		# Apply safe permissions
		chown root:root /var/lib/dietpi/dietpi-software/.GLOBAL_PW.bin
		chmod 600 /var/lib/dietpi/dietpi-software/.GLOBAL_PW.bin
	}

	# Total physical system RAM: Used to calculate percentage based value for software cache limits, e.g.: OPcache/APCu
	RAM_PHYS=$(free -m | mawk '/^Mem:/{print $2;exit}')
	# Total RAM + swap space: Used to estimate whether the swap file size needs to be increased.
	RAM_TOTAL=$(free -tm | mawk '/^Total:/{print $2;exit}')

	# Run Installation Flag (1 = run installs)
	GOSTARTINSTALL=0

	# Temporary placeholder variables
	INSTALL_URL_ADDRESS=
	UNINSTALL_URL_ADDRESS=
	DEPS_LIST=

	# Special installation vars
	USER_EMONHUB_APIKEY_COMPLETED=0
	USER_EMONHUB_APIKEY_CURRENT=0
	WIFIHOTSPOT_RTL8188C_DEVICE=0
	WIFIHOTSPOT_RTL8188C_PACKAGE=0
	USER_LINUX_AUTOINSTALL_PROMPT_DISPLAYED=0
	WIREGUARD_BUILTIN=0 # Is the WireGuard kernel module natively shipped by the kernel package?

	# PHP version specific directories, APT package-, module- and command names
	PHP_NAME='php7.3'
	FP_PHP_BASE_DIR='/etc/php/7.3'
	if (( $G_DISTRO > 5 )); then

		PHP_NAME='php7.4'
		FP_PHP_BASE_DIR='/etc/php/7.4'

	fi

	USBDRIVE=0

	# Choices Made?
	INSTALL_SOFTWARE_CHOICESMADE=0

	# DietPi Choice System: SSH Server
	# NB: Update Read_InstallFileList with defaults
	INSTALL_SSHSERVER_CHOICESMADE=0
	INDEX_SSHSERVER_CURRENT=-1
	INDEX_SSHSERVER_TARGET=-1

	# DietPi Choice System: Fileserver
	# NB: Update Read_InstallFileList with defaults
	INSTALL_FILESERVER_CHOICESMADE=0
	INDEX_FILESERVER_CURRENT=0
	INDEX_FILESERVER_TARGET=0

	# DietPi Choice System: Logging
	# NB: Update Read_InstallFileList with defaults
	INSTALL_LOGGING_CHOICESMADE=0
	INDEX_LOGGING_CURRENT=-1
	INDEX_LOGGING_TARGET=-1

	# DietPi Preference System: Webserver base
	# NB: Update Read_InstallFileList with defaults
	INDEX_WEBSERVER_CURRENT=-2
	INDEX_WEBSERVER_TARGET=-2

	# Available for (need to match highest value in dietpi-obtain_hw_model)
	readonly MAX_G_HW_MODEL=73
	readonly MAX_G_HW_ARCH=10
	#readonly MAX_G_DISTRO=6
	# - 2D array (well, bash style)
	declare -A aSOFTWARE_AVAIL_G_HW_MODEL
	declare -A aSOFTWARE_AVAIL_G_HW_ARCH
	declare -A aSOFTWARE_AVAIL_G_DISTRO

	# Generate arrays for all available software titles
	Software_Arrays_Init(){

		G_DIETPI-NOTIFY -2 'Initialising database'

		#--------------------------------------------------------------------------------
		# Categories
		# NB: Unique IDs, do not re-arrange or re-order!
		#--------------------------------------------------------------------------------
		# DietPi optimised software
		readonly aSOFTWARE_CATEGORIES_DIETPI=(

			'●─ Desktops ' #0
			'●─ Remote Desktop ' #1
			'●─ Media Systems ' #2
			'●─ BitTorrent & Download ' #3
			'●─ Cloud & Backup ' #4
			'●─ Gaming & Emulation ' #5
			'●─ Social & Search ' #6
			'●─ Camera & Surveillance ' #7
			'●─ System Stats & Management ' #8
			'●─ Remote Access ' #9
			'●─ Hardware Projects ' #10
			'●─ System Security ' #11
			'●─ Webserver Stacks ' #12
			'●─ DNS Servers ' #13
			'●─ File Servers ' #14
			'●─ VPN ' #15
			'●─ Advanced Networking ' #16
			'●─ Home Automation ' #17
			'●─ Printing ' #18
			'●─ Computational Science ' #19

		)

		# Linux additional software
		readonly aSOFTWARE_CATEGORIES_LINUX=(

			'●─ SSH Clients ' #0
			'●─ File Server Clients ' #1
			'●─ File Managers ' #2
			'●─ System ' #3
			'●─ Shared Libraries ' #4
			'●─ Networking ' #5
			'●─ Development & Programming ' #6
			'●─ Text Editors ' #7
			'●─ Desktop Utilities ' #8

		)

		#--------------------------------------------------------------------------------
		# DietPi software items
		#--------------------------------------------------------------------------------
		# Before adding, please check 'dietpi-software free' to list free IDs for use.

		# Assign unique ID to each item
		local software_id

		# Desktops
		#--------------------------------------------------------------------------------
		software_id=23

		aSOFTWARE_NAME[$software_id]='LXDE'
		aSOFTWARE_DESC[$software_id]='ultra lightweight desktop'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=0
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/desktop/#lxde'
		aSOFTWARE_REQUIRES_ALSA[$software_id]=1
		aSOFTWARE_REQUIRES_XSERVERXORG[$software_id]=1
		#------------------
		software_id=173

		aSOFTWARE_NAME[$software_id]='LXQt'
		aSOFTWARE_DESC[$software_id]='lightweight desktop'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=0
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/desktop/#lxqt'
		aSOFTWARE_REQUIRES_ALSA[$software_id]=1
		aSOFTWARE_REQUIRES_XSERVERXORG[$software_id]=1
		#------------------
		software_id=24

		aSOFTWARE_NAME[$software_id]='MATE'
		aSOFTWARE_DESC[$software_id]='desktop enviroment'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=0
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/desktop/#mate'
		aSOFTWARE_REQUIRES_ALSA[$software_id]=1
		aSOFTWARE_REQUIRES_XSERVERXORG[$software_id]=1
		#------------------
		software_id=25

		aSOFTWARE_NAME[$software_id]='Xfce'
		aSOFTWARE_DESC[$software_id]='lightweight desktop'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=0
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/desktop/#xfce'
		aSOFTWARE_REQUIRES_ALSA[$software_id]=1
		aSOFTWARE_REQUIRES_XSERVERXORG[$software_id]=1
		#------------------
		software_id=26

		aSOFTWARE_NAME[$software_id]='GNUstep'
		aSOFTWARE_DESC[$software_id]='lightweight desktop based on OpenStep'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=0
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/desktop/#gnustep'
		aSOFTWARE_REQUIRES_ALSA[$software_id]=1
		aSOFTWARE_REQUIRES_XSERVERXORG[$software_id]=1
		#------------------
		software_id=113

		aSOFTWARE_NAME[$software_id]='Chromium'
		aSOFTWARE_DESC[$software_id]='web browser for desktop or autostart'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=0
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/desktop/#chromium'
		aSOFTWARE_REQUIRES_ALSA[$software_id]=1
		aSOFTWARE_REQUIRES_XSERVERXORG[$software_id]=1
		#------------------
		software_id=174

		aSOFTWARE_NAME[$software_id]='GIMP'
		aSOFTWARE_DESC[$software_id]='mspaint on steroids'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=0
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/desktop/#gimp'
		aSOFTWARE_REQUIRES_XSERVERXORG[$software_id]=1
		#------------------
 		software_id=175

 		aSOFTWARE_NAME[$software_id]='Xfce Power Manager'
 		aSOFTWARE_DESC[$software_id]='with brightness control, recommended for LXDE/LXQt'
 		aSOFTWARE_TYPE[$software_id]=0
 		aSOFTWARE_CATEGORY_INDEX[$software_id]=0
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/desktop/#xfce-power-manager'
 		aSOFTWARE_REQUIRES_XSERVERXORG[$software_id]=1

		# Remote Desktop
		#--------------------------------------------------------------------------------
		software_id=28

		aSOFTWARE_NAME[$software_id]='TigerVNC Server'
		aSOFTWARE_DESC[$software_id]='desktop for remote connection'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=1
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/remote_desktop/#tigervnc-server'
		aSOFTWARE_REQUIRES_DESKTOP[$software_id]=1
		#------------------
		software_id=29

		aSOFTWARE_NAME[$software_id]='XRDP'
		aSOFTWARE_DESC[$software_id]='remote desktop protocol (rdp) server'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=1
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/remote_desktop/#xrdp'
		aSOFTWARE_REQUIRES_DESKTOP[$software_id]=1
		#------------------
		software_id=30

		aSOFTWARE_NAME[$software_id]='NoMachine'
		aSOFTWARE_DESC[$software_id]='multi-platform server and client access'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=1
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/remote_desktop/#nomachine'
		aSOFTWARE_REQUIRES_DESKTOP[$software_id]=1
		#------------------
		software_id=120

		aSOFTWARE_NAME[$software_id]='RealVNC Server'
		aSOFTWARE_DESC[$software_id]='desktop for remote connection'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=1
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/remote_desktop/#realvnc-server'
		aSOFTWARE_REQUIRES_DESKTOP[$software_id]=1
		# RPi only (archive.raspberrypi.org repo, libraspberrypi dependency, license)
		for ((i=10; i<=$MAX_G_HW_MODEL; i++))
		do
			aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$i]=0
		done

		# Media Systems
		#--------------------------------------------------------------------------------
		software_id=31

		aSOFTWARE_NAME[$software_id]='Kodi'
		aSOFTWARE_DESC[$software_id]='the media centre for linux'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/media/#kodi'
		aSOFTWARE_REQUIRES_ALSA[$software_id]=1
		# Odroid N2+C4 have not GPU support for Xserver, hence it's not required for Kodi
		[[ $G_HW_MODEL == 1[56] ]] || aSOFTWARE_REQUIRES_XSERVERXORG[$software_id]=1
		# Only RPi + Odroid + x86_64
		for ((i=22; i<=$MAX_G_HW_MODEL; i++))
		do
			aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$i]=0
		done
		# + ASUS TB
		aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,52]=1
		# + RK3399: Experimental
		(( $G_HW_CPUID == 3 )) && aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$G_HW_MODEL]=1
		# - Odroid C1: No HDMI with current kernel
		aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,10]=0
		# - Odroid N1/C2: No support for arm64 on Debian Buster currently, disable if not found in repo: https://dietpi.com/meveric/pool/main/k/kodi-odroid/
		#   Odroid N2/C4: Amlogic SoCs are supported via fbdev build: https://dietpi.com/meveric/pool/main/k/kodi-aml-fbdev-odroid/
		[[ $G_HW_MODEL == 1[24] ]] && ! apt-cache dumpavail | grep -qE '^P(ackage|rovides):.* kodi-odroid(,|$)' && aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$G_HW_MODEL]=0
		#------------------
		software_id=32

		aSOFTWARE_NAME[$software_id]='ympd'
		aSOFTWARE_DESC[$software_id]='lightweight web interface music player for mpd'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/media/#ympd'
		aSOFTWARE_REQUIRES_ALSA[$software_id]=1
		#------------------
		software_id=148

		aSOFTWARE_NAME[$software_id]='myMPD'
		aSOFTWARE_DESC[$software_id]='fork of ympd with improved features'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/media/#mympd'
		aSOFTWARE_REQUIRES_ALSA[$software_id]=1
		aSOFTWARE_REQUIRES_BUILDESSENTIAL[$software_id]=1
		#------------------
		software_id=119

		aSOFTWARE_NAME[$software_id]='CAVA'
		aSOFTWARE_DESC[$software_id]='optional: console audio vis for mpd'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/media/#cava'
		aSOFTWARE_REQUIRES_ALSA[$software_id]=1
		#------------------
		software_id=33

		aSOFTWARE_NAME[$software_id]='Airsonic'
		aSOFTWARE_DESC[$software_id]='web interface media streaming server'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/media/#airsonic'
		aSOFTWARE_REQUIRES_JAVA_JRE_JDK[$software_id]=1
		aSOFTWARE_REQUIRES_ALSA[$software_id]=1
		aSOFTWARE_REQUIRES_FFMPEG[$software_id]=1
		#------------------
		software_id=34

		aSOFTWARE_NAME[$software_id]='Subsonic'
		aSOFTWARE_DESC[$software_id]='web interface media streaming server'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/media/#subsonic'
		aSOFTWARE_REQUIRES_JAVA_JRE_JDK[$software_id]=1
		aSOFTWARE_REQUIRES_ALSA[$software_id]=1
		aSOFTWARE_REQUIRES_FFMPEG[$software_id]=1
		# - Buster: https://github.com/MichaIng/DietPi/issues/2787
		aSOFTWARE_AVAIL_G_DISTRO[$software_id,5]=0
		aSOFTWARE_AVAIL_G_DISTRO[$software_id,6]=0
		#------------------
		software_id=35

		aSOFTWARE_NAME[$software_id]='Logitech Media Server'
		aSOFTWARE_DESC[$software_id]='aka SlimServer, SqueezeCenter, Squeezebox Server'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/media/#logitech-media-server'
		#------------------
		software_id=36

		aSOFTWARE_NAME[$software_id]='Squeezelite'
		aSOFTWARE_DESC[$software_id]='audio player for lms & squeezebox'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		aSOFTWARE_ONLINEDOC_URL[$software_id]='p=1009#p1009'
		aSOFTWARE_REQUIRES_ALSA[$software_id]=1
		#------------------
		software_id=37

		aSOFTWARE_NAME[$software_id]='Shairport Sync'
		aSOFTWARE_DESC[$software_id]='airplay audio player with multiroom sync'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/media/#shairport-sync'
		aSOFTWARE_REQUIRES_ALSA[$software_id]=1
		#------------------
		software_id=39

		aSOFTWARE_NAME[$software_id]='ReadyMedia'
		aSOFTWARE_DESC[$software_id]='(MiniDLNA) media streaming server (dlna, upnp)'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/media/#readymedia'
		#------------------
		software_id=40

		aSOFTWARE_NAME[$software_id]='Ampache'
		aSOFTWARE_DESC[$software_id]='web interface media streaming server'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/media/#ampache'
		aSOFTWARE_REQUIRES_ALSA[$software_id]=1
		aSOFTWARE_REQUIRES_GIT[$software_id]=1
		aSOFTWARE_REQUIRES_FFMPEG[$software_id]=1
		aSOFTWARE_REQUIRES_WEBSERVER[$software_id]=1
		aSOFTWARE_REQUIRES_PHP[$software_id]=1
		aSOFTWARE_REQUIRES_MYSQL[$software_id]=1
		#------------------
		software_id=41

		aSOFTWARE_NAME[$software_id]='Emby'
		aSOFTWARE_DESC[$software_id]='web interface media streaming server'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/media/#emby'
		# - ARMv6: https://github.com/MichaIng/DietPi/issues/534#issuecomment-416405968
		aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,1]=0
		#------------------
		software_id=42

		aSOFTWARE_NAME[$software_id]='Plex Media Server'
		aSOFTWARE_DESC[$software_id]='web interface media streaming server'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/media/#plex-media-server'
		# - ARMv6: https://github.com/MichaIng/DietPi/issues/648
		aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,1]=0
		#------------------
		software_id=43

		aSOFTWARE_NAME[$software_id]='Murmur'
		aSOFTWARE_DESC[$software_id]='mumble voip server'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/media/#murmur'
		#------------------
		software_id=118

		aSOFTWARE_NAME[$software_id]='Mopidy'
		aSOFTWARE_DESC[$software_id]='web interface music & radio player'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		aSOFTWARE_ONLINEDOC_URL[$software_id]='p=3611#p3611'
		aSOFTWARE_REQUIRES_ALSA[$software_id]=1
		#------------------
		software_id=121

		aSOFTWARE_NAME[$software_id]='Roon Bridge'
		aSOFTWARE_DESC[$software_id]='Turns device into Roon capable audio player'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/media/#roon-bridge'
		aSOFTWARE_REQUIRES_ALSA[$software_id]=1
		# - ARMv6
		aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,1]=0
		#------------------
		software_id=124

		aSOFTWARE_NAME[$software_id]='NAA daemon'
		aSOFTWARE_DESC[$software_id]='signalyst network audio adaptor (naa)'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/media/#naa-daemon'
		aSOFTWARE_REQUIRES_ALSA[$software_id]=1
		#------------------
		software_id=128

		aSOFTWARE_NAME[$software_id]='MPD'
		aSOFTWARE_DESC[$software_id]='music player daemon'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		aSOFTWARE_REQUIRES_ALSA[$software_id]=1
		#------------------
		software_id=129

		aSOFTWARE_NAME[$software_id]='O!MPD'
		aSOFTWARE_DESC[$software_id]='feature-rich, web interface audio player for mpd'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/media/#ompd'
		aSOFTWARE_REQUIRES_ALSA[$software_id]=1
		aSOFTWARE_REQUIRES_WEBSERVER[$software_id]=1
		aSOFTWARE_REQUIRES_PHP[$software_id]=1
 		aSOFTWARE_REQUIRES_MYSQL[$software_id]=1
		#------------------
		software_id=135

		aSOFTWARE_NAME[$software_id]='IceCast'
		aSOFTWARE_DESC[$software_id]='Shoutcast streaming server (+DarkIce)'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/media/#icecast'
		aSOFTWARE_REQUIRES_ALSA[$software_id]=1
		# - VM
		aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,20]=0
		#------------------
		software_id=141

		aSOFTWARE_NAME[$software_id]='Spotify Connect Web'
		aSOFTWARE_DESC[$software_id]='web interface for spotify premium'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/media/#spotify-connect-web'
		aSOFTWARE_REQUIRES_ALSA[$software_id]=1
		# ARMv7 only
		for ((i=1; i<=$MAX_G_HW_ARCH; i++))
		do
			aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,$i]=0
		done
		aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,2]=1
		#------------------
		software_id=143

		aSOFTWARE_NAME[$software_id]='Koel'
		aSOFTWARE_DESC[$software_id]='web interface audio streamer'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/media/#koel'
		aSOFTWARE_REQUIRES_PHP[$software_id]=1
 		aSOFTWARE_REQUIRES_MYSQL[$software_id]=1
		aSOFTWARE_REQUIRES_NODEJS[$software_id]=1
		aSOFTWARE_REQUIRES_BUILDESSENTIAL[$software_id]=1
		aSOFTWARE_REQUIRES_FFMPEG[$software_id]=1
		# Currently user prompt asks for admin user credentials
		aSOFTWARE_REQUIRES_USERINPUT[$software_id]=1
		#------------------
		software_id=146

		aSOFTWARE_NAME[$software_id]='Tautulli'
		aSOFTWARE_DESC[$software_id]='monitoring and tracking tool for Plex'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/media/#tautulli'
 		aSOFTWARE_REQUIRES_GIT[$software_id]=1
		#------------------
		software_id=154

		aSOFTWARE_NAME[$software_id]='Roon Server'
		aSOFTWARE_DESC[$software_id]='Roon capable audio player and core'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/media/#roon-server'
		aSOFTWARE_REQUIRES_ALSA[$software_id]=1
		aSOFTWARE_REQUIRES_FFMPEG[$software_id]=1
		# x86_64 only
		for ((i=1; i<=$MAX_G_HW_ARCH; i++))
		do
			aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,$i]=0
		done
		aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,10]=1
		#------------------
		software_id=159

		aSOFTWARE_NAME[$software_id]='Allo'
		aSOFTWARE_DESC[$software_id]='web interface'
		aSOFTWARE_TYPE[$software_id]=-1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/phpbb/viewtopic.php?t=2317'
		aSOFTWARE_REQUIRES_ALSA[$software_id]=1
		aSOFTWARE_REQUIRES_WEBSERVER[$software_id]=1
		aSOFTWARE_REQUIRES_PHP[$software_id]=1
 		aSOFTWARE_REQUIRES_MYSQL[$software_id]=1
		#------------------
		software_id=160

		aSOFTWARE_NAME[$software_id]='Allo_update'
		aSOFTWARE_DESC[$software_id]='quick reinstall/update web only'
		aSOFTWARE_TYPE[$software_id]=-1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		#------------------
		software_id=163

		aSOFTWARE_NAME[$software_id]='GMediaRender'
		aSOFTWARE_DESC[$software_id]='Resource efficient UPnP/DLNA renderer'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/media/#gmediarender'
		aSOFTWARE_REQUIRES_ALSA[$software_id]=1
		#------------------
		software_id=167

		aSOFTWARE_NAME[$software_id]='Raspotify'
		aSOFTWARE_DESC[$software_id]='spotify connect client'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/media/#raspotify'
		aSOFTWARE_REQUIRES_ALSA[$software_id]=1
		# - ARMv8 - x86_64
		aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,3]=0
		aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,10]=0
		#------------------
		software_id=80

		aSOFTWARE_NAME[$software_id]='Ubooquity'
		aSOFTWARE_DESC[$software_id]='free home server for your comics and ebooks library'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/media/#ubooquity'
		aSOFTWARE_REQUIRES_JAVA_JRE_JDK[$software_id]=1
		#------------------
		software_id=179

		aSOFTWARE_NAME[$software_id]='Komga'
		aSOFTWARE_DESC[$software_id]='Free and open source comics/mangas media server with web UI'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/media/#komga'
		aSOFTWARE_REQUIRES_JAVA_JRE_JDK[$software_id]=1
		#------------------
		software_id=86

		aSOFTWARE_NAME[$software_id]='Roon Extension Manager'
		aSOFTWARE_DESC[$software_id]='Manage extensions from within Roon'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/media/#roon-extension-manager'
		aSOFTWARE_REQUIRES_GIT[$software_id]=1
		aSOFTWARE_REQUIRES_NODEJS[$software_id]=1
		#------------------
		software_id=178

		aSOFTWARE_NAME[$software_id]='Jellyfin'
		aSOFTWARE_DESC[$software_id]='FOSS web interface media streaming server'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/media/#jellyfin'
		# - ARMv6: https://github.com/jellyfin/jellyfin/issues/5011
		aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,1]=0

		# BitTorrent & Download
		#--------------------------------------------------------------------------------
		software_id=44

		aSOFTWARE_NAME[$software_id]='Transmission'
		aSOFTWARE_DESC[$software_id]='bittorrent server with web interface (c)'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=3
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/bittorrent/#transmission'
		#------------------
		software_id=45

		aSOFTWARE_NAME[$software_id]='Deluge'
		aSOFTWARE_DESC[$software_id]='bittorrent server with web interface (python)'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=3
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/bittorrent/#deluge'
		#------------------
		software_id=46

		aSOFTWARE_NAME[$software_id]='qBittorrent'
		aSOFTWARE_DESC[$software_id]='bittorrent server with web interface (c++)'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=3
		aSOFTWARE_ONLINEDOC_URL[$software_id]='p=2272#p2272'
		#------------------
		software_id=107

		aSOFTWARE_NAME[$software_id]='rTorrent'
		aSOFTWARE_DESC[$software_id]='bittorrent server with rutorrent web interface'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=3
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/bittorrent/#rtorrent'
		aSOFTWARE_REQUIRES_WEBSERVER[$software_id]=1
		aSOFTWARE_REQUIRES_PHP[$software_id]=1
		#------------------
		software_id=116

		aSOFTWARE_NAME[$software_id]='Medusa'
		aSOFTWARE_DESC[$software_id]='Automatic Video Library Manager for TV Shows'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=3
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/bittorrent/#medusa'
		# - Stretch: Python 3 > 3.5 only: https://github.com/pymedusa/Medusa#dependencies
		aSOFTWARE_AVAIL_G_DISTRO[$software_id,4]=0
		#------------------
		software_id=132

		aSOFTWARE_NAME[$software_id]='Aria2'
		aSOFTWARE_DESC[$software_id]='download manager with web interface'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=3
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/bittorrent/#arias'
		aSOFTWARE_REQUIRES_WEBSERVER[$software_id]=1
		aSOFTWARE_REQUIRES_PHP[$software_id]=1
		aSOFTWARE_REQUIRES_SQLITE[$software_id]=1
		#------------------
		software_id=139

		aSOFTWARE_NAME[$software_id]='SABnzbd'
		aSOFTWARE_DESC[$software_id]='nzb download manager'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=3
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/bittorrent/#sabnzbd'
		# Pre-compiling required on ARM
		(( $G_HW_ARCH > 9 )) || aSOFTWARE_REQUIRES_BUILDESSENTIAL[$software_id]=1
		#------------------
		software_id=142

		aSOFTWARE_NAME[$software_id]='CouchPotato'
		aSOFTWARE_DESC[$software_id]='automatically download movies'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=3
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/bittorrent/#couchpotato'
		aSOFTWARE_REQUIRES_BUILDESSENTIAL[$software_id]=1
		aSOFTWARE_REQUIRES_GIT[$software_id]=1
		# Python 2 only, hence not supported on Bullseye
		aSOFTWARE_AVAIL_G_DISTRO[$software_id,6]=0
		#------------------
		software_id=144

		aSOFTWARE_NAME[$software_id]='Sonarr'
		aSOFTWARE_DESC[$software_id]='automatically download TV shows'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=3
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/bittorrent/#sonarr'
		aSOFTWARE_REQUIRES_SQLITE[$software_id]=1
		#------------------
		software_id=145

		aSOFTWARE_NAME[$software_id]='Radarr'
		aSOFTWARE_DESC[$software_id]='automatically download movies'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=3
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/bittorrent/#radarr'
		aSOFTWARE_REQUIRES_SQLITE[$software_id]=1
		#------------------
		software_id=106

		aSOFTWARE_NAME[$software_id]='Lidarr'
		aSOFTWARE_DESC[$software_id]='automatically download music'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=3
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/bittorrent/#lidarr'
		aSOFTWARE_REQUIRES_SQLITE[$software_id]=1
		#------------------
		software_id=180

		aSOFTWARE_NAME[$software_id]='Bazarr'
		aSOFTWARE_DESC[$software_id]='automatically download subtitles'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=3
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/bittorrent/#bazarr'
		aSOFTWARE_REQUIRES_GIT[$software_id]=1
		# FFmpeg required on ARM, x86_64 binaries are shipped with the Bazarr repo: https://github.com/morpheus65535/bazarr/tree/master/bin/Linux
		# build-essential required on ARM to assure all required Python modules build successfully: https://github.com/MichaIng/DietPi/pull/3796#issuecomment-706768323
		(( $G_HW_MODEL == 10 )) || aSOFTWARE_REQUIRES_FFMPEG[$software_id]=1 aSOFTWARE_REQUIRES_BUILDESSENTIAL[$software_id]=1
		# - Stretch due to Python >= 3.7.0 requirement
		aSOFTWARE_AVAIL_G_DISTRO[$software_id,4]=0
		#------------------
		software_id=147

		aSOFTWARE_NAME[$software_id]='Jackett'
		aSOFTWARE_DESC[$software_id]='API support for your torrent trackers'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=3
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/bittorrent/#jackett'
		#------------------
		software_id=149

		aSOFTWARE_NAME[$software_id]='NZBGet'
		aSOFTWARE_DESC[$software_id]='nzb download manager'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=3
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/bittorrent/#nzbget'
		#------------------
		software_id=155

		aSOFTWARE_NAME[$software_id]='HTPC Manager'
		aSOFTWARE_DESC[$software_id]='manage your HTPC from anywhere'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=3
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/bittorrent/#htpc-manager'
		aSOFTWARE_REQUIRES_GIT[$software_id]=1
		aSOFTWARE_REQUIRES_BUILDESSENTIAL[$software_id]=1

		# Cloud & Backup
		#--------------------------------------------------------------------------------
		software_id=47

		aSOFTWARE_NAME[$software_id]='ownCloud'
		aSOFTWARE_DESC[$software_id]='File sync, sharing and collaboration platform'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=4
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/cloud/#owncloud'
		aSOFTWARE_REQUIRES_WEBSERVER[$software_id]=1
		aSOFTWARE_REQUIRES_PHP[$software_id]=1
		aSOFTWARE_REQUIRES_MYSQL[$software_id]=1
		#------------------
		software_id=114

		aSOFTWARE_NAME[$software_id]='Nextcloud'
		aSOFTWARE_DESC[$software_id]='File sync, sharing and collaboration platform'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=4
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/cloud/#nextcloud'
		aSOFTWARE_REQUIRES_WEBSERVER[$software_id]=1
		aSOFTWARE_REQUIRES_PHP[$software_id]=1
		aSOFTWARE_REQUIRES_MYSQL[$software_id]=1
		#------------------
		software_id=168

		aSOFTWARE_NAME[$software_id]='Nextcloud Talk'
		aSOFTWARE_DESC[$software_id]='Video calls with configured coTURN server'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=4
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/cloud/#nextcloud-talk'
		# Currently requires manual domain and coTURN server port input.
		# - To resolve: Default port 5349 could be used, but reliable method to get external domain/static IP is required.
		aSOFTWARE_REQUIRES_USERINPUT[$software_id]=1
		#------------------
		software_id=48

		aSOFTWARE_NAME[$software_id]='Pydio'
		aSOFTWARE_DESC[$software_id]='feature-rich backup and sync server'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=4
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/cloud/#pydio'
		aSOFTWARE_REQUIRES_WEBSERVER[$software_id]=1
		aSOFTWARE_REQUIRES_PHP[$software_id]=1
		aSOFTWARE_REQUIRES_MYSQL[$software_id]=1
		#------------------
		software_id=111

		aSOFTWARE_NAME[$software_id]='UrBackup Server'
		aSOFTWARE_DESC[$software_id]='full system backup server'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=4
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/cloud/#urbackup'
		# - ARMv6
		aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,1]=0
		#------------------
		software_id=49

		aSOFTWARE_NAME[$software_id]='Gogs'
		aSOFTWARE_DESC[$software_id]='personal github server with web interface'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=4
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/cloud/#gogs'
		aSOFTWARE_REQUIRES_GIT[$software_id]=1
		aSOFTWARE_REQUIRES_MYSQL[$software_id]=1
		#------------------
		software_id=50

		aSOFTWARE_NAME[$software_id]='Syncthing'
		aSOFTWARE_DESC[$software_id]='backup and sync server with web interface'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=4
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/cloud/#syncthing'
		#------------------
		software_id=158

		aSOFTWARE_NAME[$software_id]='MinIO'
		aSOFTWARE_DESC[$software_id]='S3 compatible distributed object server'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=4
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/cloud/#minio'
		#------------------
		software_id=161

		aSOFTWARE_NAME[$software_id]='FuguHub'
		aSOFTWARE_DESC[$software_id]='Lightweight WebDAV cloud (eg: dropbox) with a CMS'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=4
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/cloud/#fuguhub'
		# - ARMv8
		aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,3]=0
		#------------------
		software_id=165

		aSOFTWARE_NAME[$software_id]='Gitea'
		aSOFTWARE_DESC[$software_id]='Git with a cup of tea'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=4
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/cloud/#gitea'
		aSOFTWARE_REQUIRES_GIT[$software_id]=1
		aSOFTWARE_REQUIRES_MYSQL[$software_id]=1
		#------------------
		software_id=177

		aSOFTWARE_NAME[$software_id]='Firefox Sync Server'
		aSOFTWARE_DESC[$software_id]='Sync bookmarks, tabs, history & passwords'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=4
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/cloud/#firefox-sync-server'
		aSOFTWARE_REQUIRES_BUILDESSENTIAL[$software_id]=1
		aSOFTWARE_REQUIRES_SQLITE[$software_id]=1
		# - Bullseye: python(2)-virtualenv not available (yet): https://packages.debian.org/python-virtualenv
		aSOFTWARE_AVAIL_G_DISTRO[$software_id,6]=0
		#------------------
		software_id=183

		aSOFTWARE_NAME[$software_id]='Bitwarden_RS'
		aSOFTWARE_DESC[$software_id]='Unofficial Bitwarden password manager server written in Rust'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=4
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/cloud/#bitwarden_rs'
		aSOFTWARE_REQUIRES_BUILDESSENTIAL[$software_id]=1
		aSOFTWARE_REQUIRES_SQLITE[$software_id]=1

		# Gaming & Emulation
		#--------------------------------------------------------------------------------
		software_id=108

		aSOFTWARE_NAME[$software_id]='Amiberry'
		aSOFTWARE_DESC[$software_id]='Optimised Amiga emulator for ARM-based SoCs'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=5
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/gaming/#amiberry'
		aSOFTWARE_REQUIRES_ALSA[$software_id]=1
		# RPi only
		for ((i=10; i<=$MAX_G_HW_MODEL; i++))
		do
			aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$i]=0
		done
		# + Odroid XU4
		aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,11]=1
		# + ASUS TB
		aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,52]=1
		#------------------
		software_id=51

		aSOFTWARE_NAME[$software_id]='OpenTyrian'
		aSOFTWARE_DESC[$software_id]='a classic retro game, addictive'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=5
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/gaming/#opentyrian'
		aSOFTWARE_REQUIRES_ALSA[$software_id]=1
		aSOFTWARE_REQUIRES_XSERVERXORG[$software_id]=1
		# RPi only
		for ((i=10; i<=$MAX_G_HW_MODEL; i++))
		do
			aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$i]=0
		done
		# - ARMv8
		aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,3]=0
		#------------------
		software_id=112

		aSOFTWARE_NAME[$software_id]='DXX-Rebirth'
		aSOFTWARE_DESC[$software_id]='Descent 1/2'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=5
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/gaming/#dxx-rebirth'
		aSOFTWARE_REQUIRES_ALSA[$software_id]=1
		# RPi only
		for ((i=10; i<=$MAX_G_HW_MODEL; i++))
		do
			aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$i]=0
		done
		#------------------
		software_id=52

		aSOFTWARE_NAME[$software_id]='Cuberite'
		aSOFTWARE_DESC[$software_id]='Minecraft server with web interface (C++)'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=5
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/gaming/#cuberite'
		# - ARMv8
		aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,3]=0
		#------------------
		software_id=53

		aSOFTWARE_NAME[$software_id]='MineOS'
		aSOFTWARE_DESC[$software_id]='Minecraft servers with web interface (Java/Node.js)'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=5
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/gaming/#mineos'
		aSOFTWARE_REQUIRES_BUILDESSENTIAL[$software_id]=1
		aSOFTWARE_REQUIRES_GIT[$software_id]=1
		aSOFTWARE_REQUIRES_JAVA_JRE_JDK[$software_id]=1
		aSOFTWARE_REQUIRES_NODEJS[$software_id]=1
		#------------------
		software_id=156

		aSOFTWARE_NAME[$software_id]='Steam'
		aSOFTWARE_DESC[$software_id]='Valve gaming platform client'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=5
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/gaming/#steam'
		aSOFTWARE_REQUIRES_ALSA[$software_id]=1
		aSOFTWARE_REQUIRES_XSERVERXORG[$software_id]=1
		aSOFTWARE_REQUIRES_DESKTOP[$software_id]=1
		# x86_64 and ARMv7 only
		aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,1]=0
		aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,3]=0
		#------------------
		software_id=164

		aSOFTWARE_NAME[$software_id]='Nukkit'
		aSOFTWARE_DESC[$software_id]='A nuclear-powered server for Minecraft Pocket Edition'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=5
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/gaming/#nukkit'
		aSOFTWARE_REQUIRES_JAVA_JRE_JDK[$software_id]=1
		#------------------
		software_id=181

		aSOFTWARE_NAME[$software_id]='PaperMC'
		aSOFTWARE_DESC[$software_id]='Highly optimised Minecraft server with plugins, written in Java'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=5
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/gaming/#papermc'
		aSOFTWARE_REQUIRES_JAVA_JRE_JDK[$software_id]=1
		aSOFTWARE_REQUIRES_BUILDESSENTIAL[$software_id]=1
		aSOFTWARE_REQUIRES_USERINPUT[$software_id]=1

		#------------------
		software_id=62

		aSOFTWARE_NAME[$software_id]='Box86'
		aSOFTWARE_DESC[$software_id]='x86 userspace emulation'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=5
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/gaming/#box86'
		aSOFTWARE_REQUIRES_BUILDESSENTIAL[$software_id]=1
		# Only works on ARMv7
		aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,1]=0
		aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,3]=0
		aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,10]=0

		# Social & Search
		#--------------------------------------------------------------------------------
		software_id=54

		aSOFTWARE_NAME[$software_id]='phpBB'
		aSOFTWARE_DESC[$software_id]='bulletin board forum software'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=6
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/social/#phpbb'
		aSOFTWARE_REQUIRES_WEBSERVER[$software_id]=1
		aSOFTWARE_REQUIRES_PHP[$software_id]=1
		aSOFTWARE_REQUIRES_MYSQL[$software_id]=1
		#------------------
		software_id=55

		aSOFTWARE_NAME[$software_id]='Wordpress'
		aSOFTWARE_DESC[$software_id]='website blog and publishing platform'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=6
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/social/#wordpress'
		aSOFTWARE_REQUIRES_WEBSERVER[$software_id]=1
		aSOFTWARE_REQUIRES_PHP[$software_id]=1
		aSOFTWARE_REQUIRES_MYSQL[$software_id]=1
		#------------------
		software_id=38

		aSOFTWARE_NAME[$software_id]='FreshRSS'
		aSOFTWARE_DESC[$software_id]='self-hosted RSS feed aggregator'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=6
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/social/#freshrss'
		aSOFTWARE_REQUIRES_WEBSERVER[$software_id]=1
		aSOFTWARE_REQUIRES_PHP[$software_id]=1
		aSOFTWARE_REQUIRES_MYSQL[$software_id]=1
		#------------------
		software_id=56

		aSOFTWARE_NAME[$software_id]='Single File PHP Gallery'
		aSOFTWARE_DESC[$software_id]='website to host and browse your images'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=6
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/social/#single-file-php-gallery'
		aSOFTWARE_REQUIRES_WEBSERVER[$software_id]=1
		aSOFTWARE_REQUIRES_PHP[$software_id]=1
		#------------------
		software_id=57

		aSOFTWARE_NAME[$software_id]='Baïkal'
		aSOFTWARE_DESC[$software_id]='lightweight caldav + carddav server'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=6
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/social/#baikal'
		aSOFTWARE_REQUIRES_WEBSERVER[$software_id]=1
		aSOFTWARE_REQUIRES_PHP[$software_id]=1
		aSOFTWARE_REQUIRES_MYSQL[$software_id]=1
		#------------------
		software_id=58

		aSOFTWARE_NAME[$software_id]='OpenBazaar'
		aSOFTWARE_DESC[$software_id]='decentralised peer to peer bitcoin market'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=6
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/social/#openbazaar'
		aSOFTWARE_REQUIRES_BUILDESSENTIAL[$software_id]=1
		aSOFTWARE_REQUIRES_GIT[$software_id]=1
		aSOFTWARE_REQUIRES_USERINPUT[$software_id]=1
		#------------------
		software_id=133

		aSOFTWARE_NAME[$software_id]='YaCy'
		aSOFTWARE_DESC[$software_id]='decentralised open source search engine'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=6
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/social/#yacy'
		aSOFTWARE_REQUIRES_JAVA_JRE_JDK[$software_id]=1

		# Camera & Surveillance
		#--------------------------------------------------------------------------------
		software_id=59

		aSOFTWARE_NAME[$software_id]='RPi Cam Control'
		aSOFTWARE_DESC[$software_id]='Web interface & controls for your RPi camera'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=7
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/camera/#rpi-cam-control'
		aSOFTWARE_REQUIRES_WEBSERVER[$software_id]=1
		aSOFTWARE_REQUIRES_PHP[$software_id]=1
		# RPi only
		for ((i=10; i<=$MAX_G_HW_MODEL; i++))
		do
			aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$i]=0
		done
		#------------------
		software_id=136

		aSOFTWARE_NAME[$software_id]='MotionEye'
		aSOFTWARE_DESC[$software_id]='Web interface & surveillance for your camera'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=7
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/camera/#motioneye'
 		aSOFTWARE_REQUIRES_FFMPEG[$software_id]=1
		aSOFTWARE_REQUIRES_BUILDESSENTIAL[$software_id]=1
		# Python 2 only, hence not supported on Bullseye
		aSOFTWARE_AVAIL_G_DISTRO[$software_id,6]=0
		#------------------
		software_id=137

		aSOFTWARE_NAME[$software_id]='mjpg-streamer'
		aSOFTWARE_DESC[$software_id]='Simple camera streaming tool with HTML plugin'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=7
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/camera/#mjpg-streamer'

		# System Stats & Management
		#--------------------------------------------------------------------------------
		software_id=63

		aSOFTWARE_NAME[$software_id]='LinuxDash'
		aSOFTWARE_DESC[$software_id]='web interface system stats'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=8
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/system_stats/#linuxdash'
		aSOFTWARE_REQUIRES_WEBSERVER[$software_id]=1
		aSOFTWARE_REQUIRES_PHP[$software_id]=1
		#------------------
		software_id=64

		aSOFTWARE_NAME[$software_id]='phpSysInfo'
		aSOFTWARE_DESC[$software_id]='web interface system stats'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=8
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/system_stats/#phpsysinfo'
		aSOFTWARE_REQUIRES_WEBSERVER[$software_id]=1
		aSOFTWARE_REQUIRES_PHP[$software_id]=1
		#------------------
		software_id=65

		aSOFTWARE_NAME[$software_id]='Netdata'
		aSOFTWARE_DESC[$software_id]='real-time performance monitoring'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=8
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/system_stats/#netdata'
		# Node.js only required for our custom v1.11 package for Stretch ARMv6 RPi
		(( $G_HW_ARCH == 1 && $G_DISTRO < 5 )) && aSOFTWARE_REQUIRES_NODEJS[$software_id]=1
		#------------------
		software_id=66

		aSOFTWARE_NAME[$software_id]='RPi-Monitor'
		aSOFTWARE_DESC[$software_id]='web interface system stats'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=8
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/system_stats/#rpi-monitor'
		# RPi only
		for ((i=10; i<=$MAX_G_HW_MODEL; i++))
		do
			aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$i]=0
		done
		#------------------
		software_id=115

		aSOFTWARE_NAME[$software_id]='Webmin'
		aSOFTWARE_DESC[$software_id]='web interface system management'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=8
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/system_stats/#webmin'
		#------------------
		software_id=162

		aSOFTWARE_NAME[$software_id]='Docker'
		aSOFTWARE_DESC[$software_id]='Build, ship, and run distributed applications'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=8
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/programming/#docker'
		#------------------
		software_id=185

		aSOFTWARE_NAME[$software_id]='Portainer'
		aSOFTWARE_DESC[$software_id]='Simplifies container management in Docker (standalone host)'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=8
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/programming/#portainer'
		#------------------
		software_id=134

		aSOFTWARE_NAME[$software_id]='Docker Compose'
		aSOFTWARE_DESC[$software_id]='Manage multi-container Docker applications'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=8
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/programming/#docker-compose'

		# Remote Access
		#--------------------------------------------------------------------------------
		software_id=68

		aSOFTWARE_NAME[$software_id]='Remot3.it'
		aSOFTWARE_DESC[$software_id]='(Weaved) access your device over the internet'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=9
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/remote_desktop/#remot3it'
		#------------------
		software_id=138

		aSOFTWARE_NAME[$software_id]='VirtualHere'
		aSOFTWARE_DESC[$software_id]='server: share USB devices over the network'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=9
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/remote_desktop/#virtualhere'

		# Hardware Projects
		#--------------------------------------------------------------------------------
		software_id=69

		aSOFTWARE_NAME[$software_id]='RPi.GPIO'
		aSOFTWARE_DESC[$software_id]='gpio interface library for rpi (python)'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=10
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/hardware_projects/#rpigpio'
		# RPi only
		for ((i=10; i<=$MAX_G_HW_MODEL; i++))
		do
			aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$i]=0
		done
		#------------------
		software_id=70

		aSOFTWARE_NAME[$software_id]='WiringPi'
		aSOFTWARE_DESC[$software_id]='gpio interface library (c)'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=10
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/hardware_projects/#wiringpi'
		aSOFTWARE_REQUIRES_BUILDESSENTIAL[$software_id]=1
		# RPi + Odroids only
		for ((i=20; i<=$MAX_G_HW_MODEL; i++))
		do
			aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$i]=0
		done
		# + BPi Pro
		aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,51]=1
		#------------------
		software_id=71

		aSOFTWARE_NAME[$software_id]='WebIOPi'
		aSOFTWARE_DESC[$software_id]='web interface to control rpi.gpio'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=10
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/hardware_projects/#webiopi'
		aSOFTWARE_REQUIRES_BUILDESSENTIAL[$software_id]=1
		# RPi1/2/Zero only
		for ((i=3; i<=$MAX_G_HW_MODEL; i++))
		do
			aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$i]=0
		done
		#------------------
		software_id=72

		aSOFTWARE_NAME[$software_id]='I2C'
		aSOFTWARE_DESC[$software_id]='enables support for i2c based hardware'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=10
		# RPi only
		for ((i=10; i<=$MAX_G_HW_MODEL; i++))
		do
			aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$i]=0
		done
		#------------------
		software_id=100

		aSOFTWARE_NAME[$software_id]='PiJuice'
		aSOFTWARE_DESC[$software_id]='pisupply ups'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=10
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/hardware_projects/#pijuice'
		# RPi only
		for ((i=10; i<=$MAX_G_HW_MODEL; i++))
		do
			aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$i]=0
		done
		#------------------
		software_id=122

		aSOFTWARE_NAME[$software_id]='Node-RED'
		aSOFTWARE_DESC[$software_id]='tool for wiring devices, APIs and online services'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=10
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/hardware_projects/#node-red'
		aSOFTWARE_REQUIRES_NODEJS[$software_id]=1
		aSOFTWARE_REQUIRES_BUILDESSENTIAL[$software_id]=1
		#------------------
		software_id=123

		aSOFTWARE_NAME[$software_id]='Mosquitto'
		aSOFTWARE_DESC[$software_id]='MQTT messaging broker'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=10
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/hardware_projects/#mosquitto'
		#------------------
		software_id=131

		aSOFTWARE_NAME[$software_id]='Blynk Server'
		aSOFTWARE_DESC[$software_id]='msg controller for blynk mobile app and sbcs'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=10
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/hardware_projects/#blynk-server'
		aSOFTWARE_REQUIRES_JAVA_JRE_JDK[$software_id]=1
		aSOFTWARE_REQUIRES_NODEJS[$software_id]=1
		aSOFTWARE_REQUIRES_BUILDESSENTIAL[$software_id]=1
		#------------------
		software_id=166

		aSOFTWARE_NAME[$software_id]='PI-SPC'
		aSOFTWARE_DESC[$software_id]='audiophonics pi-spc power control module'
		aSOFTWARE_CATEGORY_INDEX[$software_id]=10
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/hardware_projects/#audiophonics-pi-spc'
		# RPi only
		for ((i=10; i<=$MAX_G_HW_MODEL; i++))
		do
			aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$i]=0
		done
		#------------------
		software_id=169

		aSOFTWARE_NAME[$software_id]='Google AIY'
		aSOFTWARE_DESC[$software_id]='voice kit'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=10
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/hardware_projects/#google-aiy'
		aSOFTWARE_REQUIRES_ALSA[$software_id]=1
 		aSOFTWARE_REQUIRES_GIT[$software_id]=1
		# RPi only
		for ((i=10; i<=$MAX_G_HW_MODEL; i++))
		do
			aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$i]=0
		done
		#------------------
		software_id=176

		aSOFTWARE_NAME[$software_id]='Mycroft AI'
		aSOFTWARE_DESC[$software_id]='Open Source Voice Assistant'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=10
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/hardware_projects/#mycroft-ai'
		aSOFTWARE_REQUIRES_ALSA[$software_id]=1
 		aSOFTWARE_REQUIRES_GIT[$software_id]=1
		aSOFTWARE_REQUIRES_USERINPUT[$software_id]=1
		#------------------
		software_id=74

		aSOFTWARE_NAME[$software_id]='InfluxDB'
		aSOFTWARE_DESC[$software_id]='time-series database'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=10
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/hardware_projects/#influxdb'
		#------------------
		software_id=77

		aSOFTWARE_NAME[$software_id]='Grafana'
		aSOFTWARE_DESC[$software_id]='platform for analytics and monitoring'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=10
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/hardware_projects/#grafana'

		# System Security
		#--------------------------------------------------------------------------------
		software_id=73

		aSOFTWARE_NAME[$software_id]='Fail2Ban'
		aSOFTWARE_DESC[$software_id]='prevents brute-force attacks with ip ban'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=11
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/system_security/#fail2ban'

		# Webserver Stacks
		#--------------------------------------------------------------------------------
		software_id=75

		aSOFTWARE_NAME[$software_id]='LASP'
		aSOFTWARE_DESC[$software_id]='apache2  | sqlite  | php'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=12
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/webserver_stack/#lasp-web-stack'
		#------------------
		software_id=76

		aSOFTWARE_NAME[$software_id]='LAMP'
		aSOFTWARE_DESC[$software_id]='apache2  | mariadb | php'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=12
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/webserver_stack/#lamp-web-stack'
		#------------------
		software_id=78

		aSOFTWARE_NAME[$software_id]='LESP'
		aSOFTWARE_DESC[$software_id]='nginx    | sqlite  | php'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=12
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/webserver_stack/#lesp-web-stack'
		#------------------
		software_id=79

		aSOFTWARE_NAME[$software_id]='LEMP'
		aSOFTWARE_DESC[$software_id]='nginx    | mariadb | php'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=12
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/webserver_stack/#lemp-web-stack'
		#------------------
		software_id=81

		aSOFTWARE_NAME[$software_id]='LLSP'
		aSOFTWARE_DESC[$software_id]='lighttpd | sqlite  | php'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=12
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/webserver_stack/#llsp-web-stack'
		#------------------
		software_id=82

		aSOFTWARE_NAME[$software_id]='LLMP'
		aSOFTWARE_DESC[$software_id]='lighttpd | mariadb | php'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=12
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/webserver_stack/#llmp-web-stack'
		#------------------
		software_id=83

		aSOFTWARE_NAME[$software_id]='Apache2'
		aSOFTWARE_DESC[$software_id]='webserver'
		aSOFTWARE_TYPE[$software_id]=-1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=12
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/webserver_stack/#apache2'
		aSOFTWARE_REQUIRES_PHP[$software_id]=1
		#------------------
		software_id=84

		aSOFTWARE_NAME[$software_id]='Lighttpd'
		aSOFTWARE_DESC[$software_id]='webserver'
		aSOFTWARE_TYPE[$software_id]=-1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=12
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/webserver_stack/#lighttpd'
		aSOFTWARE_REQUIRES_PHP[$software_id]=1
		#------------------
		software_id=85

		aSOFTWARE_NAME[$software_id]='Nginx'
		aSOFTWARE_DESC[$software_id]='webserver'
		aSOFTWARE_TYPE[$software_id]=-1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=12
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/webserver_stack/#nginx'
		aSOFTWARE_REQUIRES_PHP[$software_id]=1
		#------------------
		software_id=87

		aSOFTWARE_NAME[$software_id]='SQLite'
		aSOFTWARE_DESC[$software_id]='database'
		aSOFTWARE_TYPE[$software_id]=-1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=12
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/webserver_stack/#sqlite'
		#------------------
		software_id=88

		aSOFTWARE_NAME[$software_id]='MariaDB'
		aSOFTWARE_DESC[$software_id]='database'
		aSOFTWARE_TYPE[$software_id]=-1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=12
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/webserver_stack/#mariadb'
		#------------------
		software_id=89

		aSOFTWARE_NAME[$software_id]='PHP'
		aSOFTWARE_DESC[$software_id]='Hypertext Preprocessor for dynamic web content'
		aSOFTWARE_TYPE[$software_id]=-1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=12
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/webserver_stack/#php'
		#------------------
		software_id=90

		aSOFTWARE_NAME[$software_id]='phpMyAdmin'
		aSOFTWARE_DESC[$software_id]='optional mysql admin tools'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=12
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/webserver_stack/#phpmyadmin'
		aSOFTWARE_REQUIRES_WEBSERVER[$software_id]=1
		aSOFTWARE_REQUIRES_MYSQL[$software_id]=1
		aSOFTWARE_REQUIRES_PHP[$software_id]=1
		#------------------
		software_id=91

		aSOFTWARE_NAME[$software_id]='Redis'
		aSOFTWARE_DESC[$software_id]='optional non-sql database store'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=12
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/webserver_stack/#redis'
		#------------------
		software_id=92

		aSOFTWARE_NAME[$software_id]='Certbot'
		aSOFTWARE_DESC[$software_id]="Obtain and renew Let's Encrypt SSL certs for HTTPS"
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=12
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/system_security/#lets-encrypt'
		#------------------
		software_id=125

		aSOFTWARE_NAME[$software_id]='Tomcat8'
		aSOFTWARE_DESC[$software_id]='apache tomcat server'
		aSOFTWARE_CATEGORY_INDEX[$software_id]=12
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/webserver_stack/#tomcat'
		aSOFTWARE_REQUIRES_JAVA_JRE_JDK[$software_id]=1
		# - non-Raspbian Buster: https://packages.debian.org/tomcat8
		(( $G_HW_MODEL > 9 )) || (( ! $G_RASPBIAN )) && aSOFTWARE_AVAIL_G_DISTRO[$software_id,5]=0 aSOFTWARE_AVAIL_G_DISTRO[$software_id,6]=0

		# DNS Servers
		#--------------------------------------------------------------------------------
		software_id=93

		aSOFTWARE_NAME[$software_id]='Pi-hole'
		aSOFTWARE_DESC[$software_id]='block adverts for any device on your network'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=13
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/dns_servers/#pi-hole'
		aSOFTWARE_REQUIRES_GIT[$software_id]=1
		aSOFTWARE_REQUIRES_WEBSERVER[$software_id]=1
		aSOFTWARE_REQUIRES_PHP[$software_id]=1
		aSOFTWARE_REQUIRES_SQLITE[$software_id]=1
		aSOFTWARE_REQUIRES_USERINPUT[$software_id]=1
		#------------------
		software_id=182

		aSOFTWARE_NAME[$software_id]='Unbound'
		aSOFTWARE_DESC[$software_id]='validating, recursive, caching DNS resolver'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=13
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/dns_servers/#unbound'

		# File Servers
		#--------------------------------------------------------------------------------
		software_id=94

		aSOFTWARE_NAME[$software_id]='ProFTPD'
		aSOFTWARE_DESC[$software_id]='lightweight ftp server'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=14
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/file_servers/#proftpd'
		#------------------
		software_id=95

		aSOFTWARE_NAME[$software_id]='vsftpd'
		aSOFTWARE_DESC[$software_id]='alternative ftp server'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=14
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/file_servers/#vsftpd'
		#------------------
		software_id=96

		aSOFTWARE_NAME[$software_id]='Samba Server'
		aSOFTWARE_DESC[$software_id]='feature-rich file server'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=14
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/file_servers/#samba'
		#------------------
		software_id=109

		aSOFTWARE_NAME[$software_id]='NFS Server'
		aSOFTWARE_DESC[$software_id]='network file system server'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=14
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/file_servers/#nfs'

		# VPN
		#--------------------------------------------------------------------------------
		software_id=97

		aSOFTWARE_NAME[$software_id]='OpenVPN'
		aSOFTWARE_DESC[$software_id]='vpn server'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=15
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/vpn/#openvpn'
		#------------------
		software_id=172

		aSOFTWARE_NAME[$software_id]='WireGuard'
		aSOFTWARE_DESC[$software_id]='an extremely simple yet fast and modern VPN'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=15
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/vpn/#wireguard'
		# Required to ask for public domain/IP and desired VPN server port
		aSOFTWARE_REQUIRES_USERINPUT[$software_id]=1
		# RPi/Odroids/x86_64 only
		for ((i=22; i<=$MAX_G_HW_MODEL; i++))
		do
			aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$i]=0
		done
		# - Odroid U3
		aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,13]=0
		# Re-enable if module is shipped with (kernel) package
		shopt -s globstar
		for i in /lib/modules/*/kernel/**/wireguard.ko
		do
			[[ -f $i ]] || continue
			dpkg-query -S "$i" &> /dev/null || continue
			aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$G_HW_MODEL]=1 WIREGUARD_BUILTIN=1
			break
		done
		shopt -u globstar
		#------------------
		software_id=117

		aSOFTWARE_NAME[$software_id]='PiVPN'
		aSOFTWARE_DESC[$software_id]='openvpn/wireguard server install & management tool'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=15
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/vpn/#pivpn'
		aSOFTWARE_REQUIRES_USERINPUT[$software_id]=1
		aSOFTWARE_REQUIRES_GIT[$software_id]=1
		aSOFTWARE_RECOMMENDS_AUTOMATED_UPGRADES[$software_id]=1
		#------------------
		software_id=171

		aSOFTWARE_NAME[$software_id]='DietPi-NordVPN'
		aSOFTWARE_DESC[$software_id]='vpn client with connection gui'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=15
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/vpn/#dietpi-nordvpn'

		# Advanced Networking
		#--------------------------------------------------------------------------------
		software_id=60

		aSOFTWARE_NAME[$software_id]='WiFi Hotspot'
		aSOFTWARE_DESC[$software_id]='turn your device into a wifi hotspot'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=16
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/advanced_networking/#wifi-hotspot'
		#------------------
		software_id=61

		aSOFTWARE_NAME[$software_id]='Tor Hotspot'
		aSOFTWARE_DESC[$software_id]='optional: route hotspot traffic through tor'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=16
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/advanced_networking/#tor-hotspot'
		#------------------
		software_id=67

		aSOFTWARE_NAME[$software_id]='No-IP'
		aSOFTWARE_DESC[$software_id]='Dynamic DNS update client'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=16
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/advanced_networking/#no-ip'
		#------------------
		software_id=98

		aSOFTWARE_NAME[$software_id]='HAProxy'
		aSOFTWARE_DESC[$software_id]='high performance tcp/http load balancer'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=16
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/advanced_networking/#haproxy'
		aSOFTWARE_REQUIRES_BUILDESSENTIAL[$software_id]=1
		#------------------
		software_id=184

		aSOFTWARE_NAME[$software_id]='Tor Relay'
		aSOFTWARE_DESC[$software_id]='add a node to the Tor network'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=16
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/advanced_networking/#tor-relay'
		aSOFTWARE_RECOMMENDS_AUTOMATED_UPGRADES[$software_id]=1
		aSOFTWARE_REQUIRES_USERINPUT[$software_id]=1

		# Home Automation
		#--------------------------------------------------------------------------------
		software_id=99

		aSOFTWARE_NAME[$software_id]='EmonPi'
		aSOFTWARE_DESC[$software_id]='energy usage addon board with web interface'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=17
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/home_automation/#emonpi'
		# RPi only
		for ((i=10; i<=$MAX_G_HW_MODEL; i++))
		do
			aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$i]=0
		done
		# Python 2 only, hence not supported on Bullseye
		aSOFTWARE_AVAIL_G_DISTRO[$software_id,6]=0
		#------------------
		software_id=157

		aSOFTWARE_NAME[$software_id]='Home Assistant'
		aSOFTWARE_DESC[$software_id]='open-source home automation platform'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=17
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/home_automation/#home-assistant'
		#------------------
		software_id=140

		aSOFTWARE_NAME[$software_id]='Domoticz'
		aSOFTWARE_DESC[$software_id]='open-source home automation platform'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=17
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/home_automation/#domoticz'
		#------------------
		software_id=27

		aSOFTWARE_NAME[$software_id]='TasmoAdmin'
		aSOFTWARE_DESC[$software_id]='Website to manage ESP8266 devices flashed with Tasmota'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=17
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/home_automation/#tasmoadmin'
		aSOFTWARE_REQUIRES_WEBSERVER[$software_id]=1
		aSOFTWARE_REQUIRES_PHP[$software_id]=1

		# Printing
		#------------------
		software_id=153

		aSOFTWARE_NAME[$software_id]='OctoPrint'
		aSOFTWARE_DESC[$software_id]='web interface for controlling 3d printers'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=18
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/printing/#octoprint'
		aSOFTWARE_REQUIRES_BUILDESSENTIAL[$software_id]=1

		# Computational Science
		#--------------------------------------------------------------------------------
		software_id=2

		aSOFTWARE_NAME[$software_id]='Folding@Home'
		aSOFTWARE_DESC[$software_id]='distributed disease research project'
		aSOFTWARE_TYPE[$software_id]=0
		aSOFTWARE_CATEGORY_INDEX[$software_id]=19
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/computational_science/#foldinghome'
		# - ARMv6 - ARMv7
		aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,1]=0
		aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,2]=0

		#--------------------------------------------------------------------------------
		# Additional linux software items
		#--------------------------------------------------------------------------------

		# SSH Clients
		#--------------------------------------------------------------------------------
		software_id=0

		aSOFTWARE_NAME[$software_id]='OpenSSH Client'
		aSOFTWARE_DESC[$software_id]='Feature-rich SSH, SFTP and SCP client'
		aSOFTWARE_TYPE[$software_id]=1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=0

		# File Server Clients
		#--------------------------------------------------------------------------------
		software_id=1

		aSOFTWARE_NAME[$software_id]='Samba Client'
		aSOFTWARE_DESC[$software_id]='access SMB/CIFS/Samba network shares'
		aSOFTWARE_TYPE[$software_id]=1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=1
		aSOFTWARE_ONLINEDOC_URL[$software_id]=' dietpi-drive_manager > Add network drive'
		#------------------
		software_id=110

		aSOFTWARE_NAME[$software_id]='NFS Client'
		aSOFTWARE_DESC[$software_id]='network file system client'
		aSOFTWARE_TYPE[$software_id]=1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=1
		aSOFTWARE_ONLINEDOC_URL[$software_id]=' dietpi-drive_manager > Add network drive'

		# File Managers
		#--------------------------------------------------------------------------------
		software_id=3

		aSOFTWARE_NAME[$software_id]='MC'
		aSOFTWARE_DESC[$software_id]='midnight commander, powerful file manager'
		aSOFTWARE_TYPE[$software_id]=1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2
		#------------------
		software_id=4

		aSOFTWARE_NAME[$software_id]='ViFM'
		aSOFTWARE_DESC[$software_id]='file manager with vi bindings'
		aSOFTWARE_TYPE[$software_id]=1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=2

		# System
		#--------------------------------------------------------------------------------
		software_id=5

		aSOFTWARE_NAME[$software_id]='ALSA'
		aSOFTWARE_DESC[$software_id]='Advanced Linux Sound Architecture'
		aSOFTWARE_TYPE[$software_id]=1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=3
		#------------------
		software_id=6

		aSOFTWARE_NAME[$software_id]='X.Org X Server'
		aSOFTWARE_DESC[$software_id]='aka X11 - X Window System implementation'
		aSOFTWARE_TYPE[$software_id]=1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=3
		#------------------
		software_id=151

		aSOFTWARE_NAME[$software_id]='Nvidia'
		aSOFTWARE_DESC[$software_id]='Proprietary display driver for Nvidia graphics cards'
		aSOFTWARE_TYPE[$software_id]=1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=3
		aSOFTWARE_REQUIRES_XSERVERXORG[$software_id]=1
		# Native PC only
		for ((i=0; i<=$MAX_G_HW_MODEL; i++))
		do
			aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$i]=0
		done
		aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,21]=1

		# Shared Libraries
		#--------------------------------------------------------------------------------
		software_id=7

		aSOFTWARE_NAME[$software_id]='FFmpeg'
		aSOFTWARE_DESC[$software_id]='Audio & video codec libary and programs'
		aSOFTWARE_TYPE[$software_id]=1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=4
		#------------------
		software_id=8

		aSOFTWARE_NAME[$software_id]='Java'
		aSOFTWARE_DESC[$software_id]='OpenJDK runtime environment and development kit'
		aSOFTWARE_TYPE[$software_id]=1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=4
		#------------------
		software_id=9

		aSOFTWARE_NAME[$software_id]='Node.js'
		aSOFTWARE_DESC[$software_id]='JavaScript runtime environment'
		aSOFTWARE_TYPE[$software_id]=1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=4
		#------------------
		software_id=130

		aSOFTWARE_NAME[$software_id]='Python 3 pip'
		aSOFTWARE_DESC[$software_id]='Python 3 package installer'
		aSOFTWARE_TYPE[$software_id]=1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=4
		#------------------
		software_id=150

		aSOFTWARE_NAME[$software_id]='Mono'
		aSOFTWARE_DESC[$software_id]='runtime libraries and repo'
		aSOFTWARE_TYPE[$software_id]=1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=4
		#------------------
		software_id=126

		aSOFTWARE_NAME[$software_id]='LibSSL1.0.0'
		aSOFTWARE_DESC[$software_id]='for backwards compatibility on Stretch, Buster and Bullseye'
		aSOFTWARE_TYPE[$software_id]=1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=4

		# Networking
		#--------------------------------------------------------------------------------
		software_id=10

		aSOFTWARE_NAME[$software_id]='iftop'
		aSOFTWARE_DESC[$software_id]='displays bandwidth usage information'
		aSOFTWARE_TYPE[$software_id]=1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=5
		#------------------
		software_id=11

		aSOFTWARE_NAME[$software_id]='IPTraf'
		aSOFTWARE_DESC[$software_id]='interactive colorful ip lan monitor'
		aSOFTWARE_TYPE[$software_id]=1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=5
		#------------------
		software_id=12

		aSOFTWARE_NAME[$software_id]='Iperf'
		aSOFTWARE_DESC[$software_id]='internet protocol bandwidth measuring tool'
		aSOFTWARE_TYPE[$software_id]=1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=5
		#------------------
		software_id=13

		aSOFTWARE_NAME[$software_id]='MTR-Tiny'
		aSOFTWARE_DESC[$software_id]='full screen ncurses traceroute tool'
		aSOFTWARE_TYPE[$software_id]=1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=5
		#------------------
		software_id=14

		aSOFTWARE_NAME[$software_id]='nLoad'
		aSOFTWARE_DESC[$software_id]='realtime console network usage monitor'
		aSOFTWARE_TYPE[$software_id]=1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=5
		#------------------
		software_id=15

		aSOFTWARE_NAME[$software_id]='tcpdump'
		aSOFTWARE_DESC[$software_id]='command-line network traffic analyzer'
		aSOFTWARE_TYPE[$software_id]=1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=5
		#------------------
		software_id=152

		aSOFTWARE_NAME[$software_id]='Avahi-Daemon'
		aSOFTWARE_DESC[$software_id]='hostname broadcast (mac, pc bonjour)'
		aSOFTWARE_TYPE[$software_id]=1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=5

		# Development & Programming
		#--------------------------------------------------------------------------------
		software_id=16

		aSOFTWARE_NAME[$software_id]='Build-Essentials'
		aSOFTWARE_DESC[$software_id]='common packages for compiling'
		aSOFTWARE_TYPE[$software_id]=1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=6
		#------------------
		software_id=17

		aSOFTWARE_NAME[$software_id]='Git Client'
		aSOFTWARE_DESC[$software_id]='Clone and manage Git repositories locally'
		aSOFTWARE_TYPE[$software_id]=1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=6
		#------------------
		software_id=170

		aSOFTWARE_NAME[$software_id]='UnRAR'
		aSOFTWARE_DESC[$software_id]='unarchiver for .rar files'
		aSOFTWARE_TYPE[$software_id]=1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=6

		# Text Editors
		#--------------------------------------------------------------------------------
		software_id=18

		aSOFTWARE_NAME[$software_id]='Emacs'
		aSOFTWARE_DESC[$software_id]='gnu emacs editor'
		aSOFTWARE_TYPE[$software_id]=1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=7
		#------------------
		software_id=19

		aSOFTWARE_NAME[$software_id]='Jed'
		aSOFTWARE_DESC[$software_id]='editor for programmers'
		aSOFTWARE_TYPE[$software_id]=1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=7
		#------------------
		software_id=20

		aSOFTWARE_NAME[$software_id]='Vim'
		aSOFTWARE_DESC[$software_id]='vi enhanced text editor'
		aSOFTWARE_TYPE[$software_id]=1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=7
		#------------------
		software_id=21

		aSOFTWARE_NAME[$software_id]='Vim-Tiny'
		aSOFTWARE_DESC[$software_id]='compact release of vim'
		aSOFTWARE_TYPE[$software_id]=1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=7
		#------------------
		software_id=127

		aSOFTWARE_NAME[$software_id]='Neovim'
		aSOFTWARE_DESC[$software_id]='heavily refactored vim fork'
		aSOFTWARE_TYPE[$software_id]=1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=7

		# Desktop Utilities
		#--------------------------------------------------------------------------------
		software_id=22

		aSOFTWARE_NAME[$software_id]='QuiteRSS'
		aSOFTWARE_DESC[$software_id]='cross-platform, free rss reader'
		aSOFTWARE_TYPE[$software_id]=1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=8
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/desktop/#quiterss'
		aSOFTWARE_REQUIRES_DESKTOP[$software_id]=1

		#--------------------------------------------------------------------------------
		# Logging Systems (hidden)
		#--------------------------------------------------------------------------------
		software_id=101

		aSOFTWARE_NAME[$software_id]='Logrotate'
		aSOFTWARE_DESC[$software_id]='rotates log files'
		aSOFTWARE_TYPE[$software_id]=-1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=0
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/log_system/#full-logging'
		#------------------
		software_id=102

		aSOFTWARE_NAME[$software_id]='Rsyslog'
		aSOFTWARE_DESC[$software_id]='system logging'
		aSOFTWARE_TYPE[$software_id]=-1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=0
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/log_system/#full-logging'
		#------------------
		software_id=103

		aSOFTWARE_NAME[$software_id]='DietPi-RAMlog'
		aSOFTWARE_DESC[$software_id]='minimal, optimised logging'
		aSOFTWARE_TYPE[$software_id]=-1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=0
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/log_system/#dietpi-ramlog'

		#--------------------------------------------------------------------------------
		# SSH Servers (hidden)
		#--------------------------------------------------------------------------------
		software_id=104

		aSOFTWARE_NAME[$software_id]='Dropbear'
		aSOFTWARE_DESC[$software_id]='Lightweight SSH server'
		aSOFTWARE_TYPE[$software_id]=-1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=0
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/ssh/#dropbear'
		#------------------
		software_id=105

		aSOFTWARE_NAME[$software_id]='OpenSSH Server'
		aSOFTWARE_DESC[$software_id]='Feature-rich SSH server with SFTP and SCP support'
		aSOFTWARE_TYPE[$software_id]=-1
		aSOFTWARE_CATEGORY_INDEX[$software_id]=0
		aSOFTWARE_ONLINEDOC_URL[$software_id]='https://dietpi.com/docs/software/ssh/#openssh'

		#--------------------------------------------------------------------------------
		# Init install state for defined software
		for i in "${!aSOFTWARE_NAME[@]}"
		do
			aSOFTWARE_INSTALL_STATE[$i]=0
		done
		# - By default installed on DietPi images:
		aSOFTWARE_INSTALL_STATE[103]=2 # DietPi-RAMlog
		aSOFTWARE_INSTALL_STATE[104]=2 # Dropbear
		#--------------------------------------------------------------------------------

		G_DIETPI-NOTIFY 0 'Initialised database'

	}

	# Disable software installation, if user input is required for automated installs
	Install_Disable_Requires_UserInput(){

		(( $G_INTERACTIVE )) && return

		for i in "${!aSOFTWARE_NAME[@]}"
		do
			if (( ${aSOFTWARE_INSTALL_STATE[$i]} == 1 && ${aSOFTWARE_REQUIRES_USERINPUT[$i]:-0} == 1 ))
			then
				# Disable
				aSOFTWARE_INSTALL_STATE[$i]=0
				G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$i]}: Install requires user input and cannot be automated."
				G_DIETPI-NOTIFY 1 "${aSOFTWARE_NAME[$i]}: Please run 'dietpi-software' to install manually."
			fi
		done

	}

	# Work out which additional software we need to install
	# - We do reinstall most =2 marked software as well, just to be sure.
	Install_Flag_Prereq_Software(){

		G_DIETPI-NOTIFY 3 "$G_PROGRAM_NAME" 'Checking for prerequisite software'

		local software_id=-1

		#-------------------------------------------------------------------------
		# Pre-req software, for items that do NOT have their own array aSOFTWARE_REQUIRES_SOFTWARENAME

		# Don't let Tor Relay and Tor Hotspot be installed at the same time
		if (( ${aSOFTWARE_INSTALL_STATE[184]} > 0 && ${aSOFTWARE_INSTALL_STATE[61]} > 0 ))
		then
			(( ${aSOFTWARE_INSTALL_STATE[61]} == 1 )) && aSOFTWARE_INSTALL_STATE[61]=0
			(( ${aSOFTWARE_INSTALL_STATE[184]} == 1 )) && aSOFTWARE_INSTALL_STATE[184]=0
			G_WHIP_MSG "[WARNING] Conflicting software selection
\n${aSOFTWARE_NAME[61]} and ${aSOFTWARE_NAME[184]} cannot be installed at the same time.
\nThese pieces of software will be skipped. Other DietPi-Software installations will continue."
		fi

		# Docker
		# - Portainer (185)
		# - Docker Compose (134)
		software_id=162
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} < 1 )) &&
		   (( ${aSOFTWARE_INSTALL_STATE[185]} == 1 ||
		      ${aSOFTWARE_INSTALL_STATE[134]} == 1 ))
		then
			aSOFTWARE_INSTALL_STATE[$software_id]=1
			G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} will be installed"
		fi

		# Prevent Docker install if kernel has just been upgraded and old one overwritten/purged: https://github.com/MichaIng/DietPi/issues/3126
		if [[ ${aSOFTWARE_INSTALL_STATE[162]} == 1 && ! -d /lib/modules/$(uname -r) ]]
		then
			G_WHIP_MSG '[WARNING] Docker install will be deselected
\nIt seems that your kernel has just been upgraded. The mismatch between loaded and installed kernel versions would cause a failure during Docker install.
\nPlease reselect Docker from dietpi-software after next reboot, or run:
# dietpi-software install 162'
			aSOFTWARE_INSTALL_STATE[162]=0
			(( ${aSOFTWARE_INSTALL_STATE[185]} == 1 )) && aSOFTWARE_INSTALL_STATE[185]=0
		fi

		# Nextcloud extensions
		# - Nextcloud Talk (168)
		software_id=114
		if (( ${aSOFTWARE_INSTALL_STATE[168]} == 1 && ${aSOFTWARE_INSTALL_STATE[$software_id]} < 1 )); then

			aSOFTWARE_INSTALL_STATE[$software_id]=1
			G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} will be installed"

		fi

		# Tor Hotspot requires WiFi Hotspot
		software_id=60
		if (( ${aSOFTWARE_INSTALL_STATE[61]} == 1 )); then

			aSOFTWARE_INSTALL_STATE[$software_id]=1
			G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} will be installed"

		fi

		# If OctoPrint and mjpg-streamer both are installed, OctoPrint is automatically configured to use mjpg-streamer. For the integrated time-lapse feature, FFmpeg is required.
		software_id=7
		if (( ${aSOFTWARE_INSTALL_STATE[137]} > 0 && ${aSOFTWARE_INSTALL_STATE[153]} > 0 && ${aSOFTWARE_INSTALL_STATE[137]} + ${aSOFTWARE_INSTALL_STATE[153]} < 4 ))
		then
			aSOFTWARE_INSTALL_STATE[$software_id]=1
			G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} will be installed"
		fi

		# Software that requires WiringPi
		# - Audiophonics Pi-SPC (166)
		software_id=70
		if (( ${aSOFTWARE_INSTALL_STATE[166]} == 1 )); then

			aSOFTWARE_INSTALL_STATE[$software_id]=1
			G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} will be installed"

		fi

		# Software that requires I2C
		# - PiJuice (100)
		software_id=72
		if (( ${aSOFTWARE_INSTALL_STATE[100]} == 1 )); then

			aSOFTWARE_INSTALL_STATE[$software_id]=1
			G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} will be installed"

		fi

		# Software required by Allo Web Interface
		if (( ${aSOFTWARE_INSTALL_STATE[159]} == 1 )); then

			aSOFTWARE_INSTALL_STATE[36]=1 # Squeezelite
			aSOFTWARE_INSTALL_STATE[37]=1 # Shairport Sync
			aSOFTWARE_INSTALL_STATE[65]=1 # Netdata
			aSOFTWARE_INSTALL_STATE[96]=1 # Samba
			aSOFTWARE_INSTALL_STATE[121]=1 # Roon Bridge
			aSOFTWARE_INSTALL_STATE[124]=1 # NAA Daemon
			#aSOFTWARE_INSTALL_STATE[128]=1 # MPD (pulled in by O!MPD)
			aSOFTWARE_INSTALL_STATE[129]=1 # O!MPD
			#aSOFTWARE_INSTALL_STATE[152]=1 # Avahi-Daemon (pulled in by O!MPD and Shairport-Sync)
			aSOFTWARE_INSTALL_STATE[163]=1 # GMediaRender

		fi

		# Software required by Google AIY
		if (( ${aSOFTWARE_INSTALL_STATE[169]} == 1 )); then

			aSOFTWARE_INSTALL_STATE[69]=1 # RPi.GPIO
			G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[69]} will be installed"
			#aSOFTWARE_INSTALL_STATE[130]=1 # Python 3 pip, enabled in "Software that requires Python 3 pip"

		fi

		# Software that requires Avahi-Daemon
		# - Shairport-Sync (37)
		software_id=152
		if (( ${aSOFTWARE_INSTALL_STATE[31]} == 1 ||
			${aSOFTWARE_INSTALL_STATE[37]} == 1 ||
			${aSOFTWARE_INSTALL_STATE[128]} == 1 ||
			${aSOFTWARE_INSTALL_STATE[138]} == 1 ||
			${aSOFTWARE_INSTALL_STATE[163]} == 1 )); then

			aSOFTWARE_INSTALL_STATE[$software_id]=1
			G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} will be installed"

		fi

		# Software that requires LibSSL1.0.0
		# - WiFi Hotspot (60, our custom compiled RTL8188C* binaries only)
		#	Check for RTL8188C* device, which requires a different driver: https://github.com/pritambaral/hostapd-rtl871xdrv#why
		#	ARM-only, since we have no x86 binaries
		#	Exclude RPi, since compatible binaries are shipped with archive.raspberrypi.org
		if (( ${aSOFTWARE_INSTALL_STATE[60]} == 1 )) && lsusb | grep -qi 'RTL8188C'; then

			WIFIHOTSPOT_RTL8188C_DEVICE=1
			# Some repos (e.g. ARMbian) provide special packages
			apt-cache dumpavail | grep -qE '^P(ackage|rovides):.* hostapd-realtek(,|$)' && WIFIHOTSPOT_RTL8188C_PACKAGE=1

		fi
		# - Domoticz (140): https://github.com/MichaIng/DietPi/issues/129#issuecomment-596255110
		# - Jackett (147)
		software_id=126
		if (( ( ${aSOFTWARE_INSTALL_STATE[60]} == 1 && $WIFIHOTSPOT_RTL8188C_DEVICE && ! $WIFIHOTSPOT_RTL8188C_PACKAGE && $G_HW_MODEL > 9 && $G_HW_ARCH < 4 ) ||
			${aSOFTWARE_INSTALL_STATE[140]} == 1 ||
			${aSOFTWARE_INSTALL_STATE[147]} == 1 )); then

			aSOFTWARE_INSTALL_STATE[$software_id]=1
			G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} will be installed"

		fi

		# Software that requires Mono
		# - Lidarr (106)
		# - Sonarr (144)
		# - Radarr (145) on ARMv6
		# - Jackett (147) on ARMv6: https://github.com/Jackett/Jackett#installation-on-linux-armv6-or-below
		software_id=150
		if (( ${aSOFTWARE_INSTALL_STATE[106]} == 1 ||
			${aSOFTWARE_INSTALL_STATE[144]} == 1 ||
			( ${aSOFTWARE_INSTALL_STATE[145]} == 1 && $G_HW_ARCH == 1 ) ||
			( ${aSOFTWARE_INSTALL_STATE[147]} == 1 && $G_HW_ARCH == 1 ) )); then

			aSOFTWARE_INSTALL_STATE[$software_id]=1
			G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} will be installed"

		fi

		# Software that requires Python 3 pip: https://github.com/MichaIng/DietPi/issues/784
		# - Mopidy (118) from Buster on
		# - SABnzbd (139)
		# - OctoPrint (153) from Buster on
		# - HTPC Manager (155)
		# - Google AIY (169)
		# - Bazarr (180)
		# - Docker Compose (134)
		software_id=130
		if (( ( ${aSOFTWARE_INSTALL_STATE[118]} == 1 && $G_DISTRO > 4 ) ||
			${aSOFTWARE_INSTALL_STATE[139]} == 1 ||
		      ( ${aSOFTWARE_INSTALL_STATE[153]} == 1 && $G_DISTRO > 4 ) ||
			${aSOFTWARE_INSTALL_STATE[155]} == 1 ||
			${aSOFTWARE_INSTALL_STATE[169]} == 1 ||
			${aSOFTWARE_INSTALL_STATE[180]} == 1 ||
			${aSOFTWARE_INSTALL_STATE[134]} == 1 )); then

			aSOFTWARE_INSTALL_STATE[$software_id]=1
			G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} will be installed"

		fi

		# Software that requires MPD
		# - ympd
		# - Cava
		# - O!MPD
		# - myMPD
		software_id=128
		if (( ${aSOFTWARE_INSTALL_STATE[32]} == 1 ||
			${aSOFTWARE_INSTALL_STATE[119]} == 1 ||
			${aSOFTWARE_INSTALL_STATE[129]} == 1 ||
			${aSOFTWARE_INSTALL_STATE[148]} == 1 )); then

			aSOFTWARE_INSTALL_STATE[$software_id]=1
			G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} will be installed"

		fi

		# Software that requires Redis
		# - ownCloud
		# - Nextcloud
		software_id=91
		if (( ${aSOFTWARE_INSTALL_STATE[47]} == 1 ||
			${aSOFTWARE_INSTALL_STATE[114]} == 1 )); then

			aSOFTWARE_INSTALL_STATE[$software_id]=1
			G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} will be installed"

		fi

		# Software that requires UnRAR
		# - rTorrent
		# - Medusa
		# - SABnzbd (139)
		# - Bazarr (180) on ARMv6/7: https://github.com/morpheus65535/bazarr/tree/master/bin/Linux
		software_id=170
		if (( ${aSOFTWARE_INSTALL_STATE[107]} == 1 ||
			${aSOFTWARE_INSTALL_STATE[116]} == 1 ||
			${aSOFTWARE_INSTALL_STATE[139]} == 1 ||
		      ( ${aSOFTWARE_INSTALL_STATE[180]} == 1 && $G_HW_ARCH < 3 ) )); then

			aSOFTWARE_INSTALL_STATE[$software_id]=1
			G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} will be installed"

		fi

		# Software that requires Box86 on ARM: Do not reinstall Box86 if present already!
		# - Steam (156)
		software_id=62
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} < 2 && $G_HW_ARCH == 2 &&
			${aSOFTWARE_INSTALL_STATE[156]} == 1 )); then

			aSOFTWARE_INSTALL_STATE[$software_id]=1
			G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} will be installed"

		fi

		#-------------------------------------------------------------------------
		# WEBSERVER - Manual stack install
		# - Define extra DietPi install flags for WEBSERVER_STACKS
		# LLMP
		if (( ${aSOFTWARE_INSTALL_STATE[82]} == 1 )); then

			aSOFTWARE_INSTALL_STATE[84]=1
			aSOFTWARE_INSTALL_STATE[88]=1
			aSOFTWARE_INSTALL_STATE[89]=1

		fi

		# LLSP
		if (( ${aSOFTWARE_INSTALL_STATE[81]} == 1 )); then

			aSOFTWARE_INSTALL_STATE[84]=1
			aSOFTWARE_INSTALL_STATE[87]=1
			aSOFTWARE_INSTALL_STATE[89]=1

		fi

		# LEMP
		if (( ${aSOFTWARE_INSTALL_STATE[79]} == 1 )); then

			aSOFTWARE_INSTALL_STATE[85]=1
			aSOFTWARE_INSTALL_STATE[88]=1
			aSOFTWARE_INSTALL_STATE[89]=1

		fi

		# LESP
		if (( ${aSOFTWARE_INSTALL_STATE[78]} == 1 )); then

			aSOFTWARE_INSTALL_STATE[85]=1
			aSOFTWARE_INSTALL_STATE[87]=1
			aSOFTWARE_INSTALL_STATE[89]=1

		fi

		# LAMP
		if (( ${aSOFTWARE_INSTALL_STATE[76]} == 1 )); then

			aSOFTWARE_INSTALL_STATE[83]=1
			aSOFTWARE_INSTALL_STATE[88]=1
			aSOFTWARE_INSTALL_STATE[89]=1

		fi

		# LASP
		if (( ${aSOFTWARE_INSTALL_STATE[75]} == 1 )); then

			aSOFTWARE_INSTALL_STATE[83]=1
			aSOFTWARE_INSTALL_STATE[87]=1
			aSOFTWARE_INSTALL_STATE[89]=1

		fi

		#-------------------------------------------------------------------------
		# Pre-req software, for items that DO have their own array aSOFTWARE_REQUIRES_SOFTWARENAME

		for i in "${!aSOFTWARE_NAME[@]}"
		do

			(( ${aSOFTWARE_INSTALL_STATE[$i]} == 1 )) || continue

			# Webserver - Auto install via choice system
			# - Check for existing webserver base (Apache2, Nginx, Lighttpd) installation
			if (( ${aSOFTWARE_REQUIRES_WEBSERVER[$i]:=0} &&
				${aSOFTWARE_INSTALL_STATE[83]} < 1 &&
				${aSOFTWARE_INSTALL_STATE[84]} < 1 &&
				${aSOFTWARE_INSTALL_STATE[85]} < 1 )); then

				# None found, select one for install, based on user preference
				# - Apache
				if (( $INDEX_WEBSERVER_TARGET == 0 )); then

					aSOFTWARE_INSTALL_STATE[83]=1
					G_DIETPI-NOTIFY 2 'Apache2 will be installed'

				# - Nginx
				elif (( $INDEX_WEBSERVER_TARGET == -1 )); then

					aSOFTWARE_INSTALL_STATE[85]=1
					G_DIETPI-NOTIFY 2 'Nginx will be installed'

				# - Lighttpd
				else

					aSOFTWARE_INSTALL_STATE[84]=1
					G_DIETPI-NOTIFY 2 'Lighttpd will be installed'

				fi

				# Pretent software requiring PHP, to mark it below, in case webserver ID already passed
				aSOFTWARE_REQUIRES_PHP[$i]=1

			fi

			software_id=89 # PHP
			if (( ${aSOFTWARE_REQUIRES_PHP[$i]:=0} && aSOFTWARE_INSTALL_STATE[$software_id] != 1 )); then

				aSOFTWARE_INSTALL_STATE[$software_id]=1
				G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} will be installed"

			fi

			software_id=88 # MariaDB
			if (( ${aSOFTWARE_REQUIRES_MYSQL[$i]:=0} && aSOFTWARE_INSTALL_STATE[$software_id] != 1 )); then

				aSOFTWARE_INSTALL_STATE[$software_id]=1
				G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} will be installed"

			fi

			software_id=87 # SQLite
			if (( ${aSOFTWARE_REQUIRES_SQLITE[$i]:=0} && aSOFTWARE_INSTALL_STATE[$software_id] != 1 )); then

				aSOFTWARE_INSTALL_STATE[$software_id]=1
				G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} will be installed"

			fi

			# Desktop
			if (( ${aSOFTWARE_REQUIRES_DESKTOP[$i]:=0} &&
				${aSOFTWARE_INSTALL_STATE[23]} < 1 &&
				${aSOFTWARE_INSTALL_STATE[24]} < 1 &&
				${aSOFTWARE_INSTALL_STATE[25]} < 1 &&
				${aSOFTWARE_INSTALL_STATE[26]} < 1 &&
				${aSOFTWARE_INSTALL_STATE[173]} < 1 )); then

				# If no desktop is selected or installed, default to LXDE
				aSOFTWARE_INSTALL_STATE[23]=1
				G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[23]} will be installed"

				# Pretent software requiring Xserver, to mark it below, in case desktop ID already passed
				aSOFTWARE_REQUIRES_XSERVERXORG[$i]=1

			fi

			software_id=17 # Git Client
			if (( ${aSOFTWARE_REQUIRES_GIT[$i]:=0} && aSOFTWARE_INSTALL_STATE[$software_id] != 1 )); then

				aSOFTWARE_INSTALL_STATE[$software_id]=1
				G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} will be installed"
			fi

			software_id=16 # Build essentials
			if (( ${aSOFTWARE_REQUIRES_BUILDESSENTIAL[$i]:=0} && aSOFTWARE_INSTALL_STATE[$software_id] != 1 )); then

				aSOFTWARE_INSTALL_STATE[$software_id]=1
				G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} will be installed"

			fi

			software_id=7 # FFmpeg
			if (( ${aSOFTWARE_REQUIRES_FFMPEG[$i]:=0} && aSOFTWARE_INSTALL_STATE[$software_id] != 1 )); then

				aSOFTWARE_INSTALL_STATE[$software_id]=1
				G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} will be installed"

			fi

			software_id=8 # Java
			if (( ${aSOFTWARE_REQUIRES_JAVA_JRE_JDK[$i]:=0} && aSOFTWARE_INSTALL_STATE[$software_id] != 1 )); then

				aSOFTWARE_INSTALL_STATE[$software_id]=1
				G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} will be installed"

			fi

			software_id=9 # Node.js
			if (( ${aSOFTWARE_REQUIRES_NODEJS[$i]:=0} && aSOFTWARE_INSTALL_STATE[$software_id] != 1 )); then

				aSOFTWARE_INSTALL_STATE[$software_id]=1
				G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} will be installed"

			fi

			software_id=5 # ALSA
			if (( ${aSOFTWARE_REQUIRES_ALSA[$i]:=0} && aSOFTWARE_INSTALL_STATE[$software_id] != 1 )); then

				aSOFTWARE_INSTALL_STATE[$software_id]=1
				G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} will be installed"

			fi

			software_id=6 # Xserver
			if (( ${aSOFTWARE_REQUIRES_XSERVERXORG[$i]:=0} && aSOFTWARE_INSTALL_STATE[$software_id] != 1 )); then

				aSOFTWARE_INSTALL_STATE[$software_id]=1
				G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} will be installed"

			fi

			# Software that recommends automated APT upgrades
			if (( ${aSOFTWARE_RECOMMENDS_AUTOMATED_UPGRADES[$i]:=0} )) && ! grep -q '^[[:blank:]]*CONFIG_CHECK_APT_UPDATES=2' /boot/dietpi.txt
			then
				G_WHIP_BUTTON_OK_TEXT='YES' G_WHIP_BUTTON_CANCEL_TEXT='NO'
				if G_WHIP_YESNO "Would you like to enable automated daily upgrades for APT packages?\n\nThis is recommended for ${aSOFTWARE_NAME[$i]}.
\nDaily APT upgrade logs can be found at: /var/tmp/dietpi/logs/dietpi-update_apt.log"
				then
					G_CONFIG_INJECT 'CONFIG_CHECK_APT_UPDATES=' 'CONFIG_CHECK_APT_UPDATES=2' /boot/dietpi.txt
				fi
			fi

		done

		# Webserver - Check for stacks and flag as installing
		# - Apache
		if (( ${aSOFTWARE_INSTALL_STATE[83]} > 0 )); then

			# SQLite: LASP
			(( ${aSOFTWARE_INSTALL_STATE[87]} > 0 )) && aSOFTWARE_INSTALL_STATE[75]=1

			# MariaDB: LAMP
			(( ${aSOFTWARE_INSTALL_STATE[88]} > 0 )) && aSOFTWARE_INSTALL_STATE[76]=1

		# - Nginx
		elif (( ${aSOFTWARE_INSTALL_STATE[85]} > 0 )); then

			# SQLite: LESP
			(( ${aSOFTWARE_INSTALL_STATE[87]} > 0 )) && aSOFTWARE_INSTALL_STATE[78]=1

			# MariaDB: LEMP
			(( ${aSOFTWARE_INSTALL_STATE[88]} > 0 )) && aSOFTWARE_INSTALL_STATE[79]=1

		# - Lighttpd
		elif (( ${aSOFTWARE_INSTALL_STATE[84]} > 0 )); then

			# SQLite: LLSP
			(( ${aSOFTWARE_INSTALL_STATE[87]} > 0 )) && aSOFTWARE_INSTALL_STATE[81]=1

			# MariaDB: LLMP
			(( ${aSOFTWARE_INSTALL_STATE[88]} > 0 )) && aSOFTWARE_INSTALL_STATE[82]=1

		fi

		# Update PHP variables after all software titles have been marked
		# - Never install PHP7.2 if PHP7.3 or PHP7.4 is already present
		if (( ${aSOFTWARE_INSTALL_STATE[89]} > 0 )) && ! command -v php7.3 > /dev/null && ! command -v php7.4 > /dev/null; then

			# Do not upgrade PHP7.2, since we can support it for a long time
			if command -v php7.2 > /dev/null; then

				PHP_NAME='php7.2'
				FP_PHP_BASE_DIR='/etc/php/7.2'

			# ownCloud up to v10.2
			elif [[ -f '/var/www/owncloud/version.php' ]]; then

				# shellcheck disable=SC2016
				local oc_version_major=$(sed -n '/$OC_VersionString/{s/^[^0-9]*//;s/\..*$//;p;q}' /var/www/owncloud/version.php)
				# shellcheck disable=SC2016
				local oc_version_minor=$(sed -n '/$OC_VersionString/{s/^[^.]*\.//;s/\..*$//;p;q}' /var/www/owncloud/version.php)
				if (( $oc_version_major < 10 || ( $oc_version_major == 10 && $oc_version_minor < 3 ) )); then

					PHP_NAME='php7.2'
					FP_PHP_BASE_DIR='/etc/php/7.2'

				fi

			fi

		fi

	}

	Create_Desktop_Shared_Items(){

		# Pre-create dirs
		G_EXEC mkdir -p /usr/share/applications /var/lib/dietpi/dietpi-software/installed/desktop/{icons,wallpapers}

		# Icons
		G_THREAD_START curl -sSfL 'https://raw.githubusercontent.com/MichaIng/DietPi-Website/master/images/dietpi-logo_128x128.png' -o /var/lib/dietpi/dietpi-software/installed/desktop/icons/dietpi-icon.png
		G_THREAD_START curl -sSfL "https://raw.githubusercontent.com/$G_GITOWNER/DietPi/$G_GITBRANCH/.conf/desktop/icons/grey_16x16.png" -o /var/lib/dietpi/dietpi-software/installed/desktop/icons/grey_16x16.png
		G_THREAD_START curl -sSfL "https://raw.githubusercontent.com/$G_GITOWNER/DietPi/$G_GITBRANCH/.conf/desktop/icons/justboom.png" -o /var/lib/dietpi/dietpi-software/installed/desktop/icons/justboom.png

		# Wallpapers
		G_THREAD_START curl -sSfL "https://raw.githubusercontent.com/$G_GITOWNER/DietPi/$G_GITBRANCH/.conf/desktop/wallpapers/dietpi-logo_inverted_1080p.png" -o /var/lib/dietpi/dietpi-software/installed/desktop/wallpapers/dietpi-logo_inverted_1080p.png
		G_THREAD_START curl -sSfL "https://raw.githubusercontent.com/$G_GITOWNER/DietPi/$G_GITBRANCH/.conf/desktop/wallpapers/dietpi-logo_1080p.png" -o /var/lib/dietpi/dietpi-software/installed/desktop/wallpapers/dietpi-logo_1080p.png

		# File manager bookmarks
		G_THREAD_START curl -sSfL "https://raw.githubusercontent.com/$G_GITOWNER/DietPi/$G_GITBRANCH/.conf/desktop/gtk/.gtk-bookmarks" -o /var/lib/dietpi/dietpi-software/installed/desktop/.gtk-bookmarks

		# Desktop applications
		local adesktop_items=(

			'dietpi-software'
			'dietpi-drive_manager'
			'dietpi-update'
			'dietpi-config'
			'dietpi-backup'
			'dietpi-sync'
			'dietpi-services'
			'dietpi-cleaner'
			'dietpi-cron'
			'dietpi-launcher'
			'dietpi-justboom'
			'es2_info'
			'es2_gears'
			'glx_info'
			'glx_gears'

		)

		for i in "${adesktop_items[@]}"
		do
			G_THREAD_START curl -sSfL "https://raw.githubusercontent.com/$G_GITOWNER/DietPi/$G_GITBRANCH/.conf/desktop/apps/$i.desktop" -o "/usr/share/applications/$i.desktop"
		done

		# Create autostart script to add default desktop icons and configs to users that login the first time into a desktop
		cat << '_EOF_' > /var/lib/dietpi/dietpi-software/installed/desktop/dietpi-desktop_setup.sh
#!/bin/dash

# Create desktop shortcuts
set 'opentyrian' 'kodi' 'steam' 'dxx-rebirth' 'chromium' 'chromium-browser' 'htop'
[ $USER = 'root' ] && set "$@" 'dietpi-launcher' 'dietpi-software' 'dietpi-config'
[ -d ~/Desktop ] || mkdir -p ~/Desktop
for i in "$@"
do
	[ -f /usr/share/applications/$i.desktop ] && ! [ -f ~/Desktop/$i.desktop ] && ln -sf /usr/share/applications/$i.desktop ~/Desktop/$i.desktop
done

# Create file manager bookmarks
if ! [ -f ~/.gtk-bookmarks ]
then
	cp /var/lib/dietpi/dietpi-software/installed/desktop/.gtk-bookmarks ~/.gtk-bookmarks
	sed -i "s|/root|$HOME|g" ~/.gtk-bookmarks
fi

# Disable this autostart entry
mkdir -p ~/.config/autostart
echo -e '[Desktop Entry]\nHidden=true' > ~/.config/autostart/dietpi-desktop_setup.desktop

# Apply desktop-specific configs
if [ $XDG_CURRENT_DESKTOP = 'LXDE' ]
then
	pcmanfm --desktop-off
	sleep 0.2
	sed -i '/^desktop_shadow=/c\desktop_shadow=#333333' ~/.config/pcmanfm/LXDE/desktop-items-0.conf
	nohup pcmanfm --desktop -p LXDE &
	sleep 0.2
	pcmanfm -w /var/lib/dietpi/dietpi-software/installed/desktop/wallpapers/dietpi-logo_inverted_1080p.png
fi
_EOF_

		cat << '_EOF_' > /etc/xdg/autostart/dietpi-desktop_setup.desktop
[Desktop Entry]
Version=1.0
Name=DietPi-Desktop_setup
Type=Application
Comment=Adds default desktop entries and configs to the users desktop
NoDisplay=true
Exec=/var/lib/dietpi/dietpi-software/installed/desktop/dietpi-desktop_setup.sh
Icon=/var/lib/dietpi/dietpi-software/installed/desktop/icons/dietpi-icon.png
_EOF_

		# Add execute permissions to prevent "untrusted" prompt in MATE and possibily other desktops
		G_EXEC chmod +x /usr/share/applications/*.desktop /var/lib/dietpi/dietpi-software/installed/desktop/dietpi-desktop_setup.sh

		G_THREAD_WAIT

	}

	# Add desktop entry for users which logged into a desktop already and hence have DietPi-Desktop_setup disabled.
	# $1 = Application name
	Create_Desktop_Shortcut()
	{
		local uid gid app=$1
		for i in /root /home/*
		do
			[[ -f $i/.config/autostart/dietpi-desktop_setup.desktop && ! -f $i/Desktop/$app.desktop ]] || continue
			if [[ ! -d $i/Desktop ]]
			then
				# shellcheck disable=SC2012
				read -r uid gid <<< "$(ls -dn "$i" | mawk '{print $3" "$4}')"
				G_EXEC mkdir -p "$i/Desktop"
				G_EXEC chown "$uid:$gid" "$i/Desktop"
			fi
			G_EXEC ln -sf "/usr/share/applications/$app.desktop" "$i/Desktop/$app.desktop"
		done
	}

	Create_UserContent_Folders(){

		G_EXEC mkdir -p /mnt/dietpi_userdata/{Music,Pictures,Video,downloads} /var/www /opt
		G_EXEC chown dietpi:dietpi /mnt/dietpi_userdata/{Music,Pictures,Video,downloads}
		G_EXEC chmod 775 /mnt/dietpi_userdata/{Music,Pictures,Video,downloads}

	}

	Download_Test_Media(){

		[[ -f '/mnt/dietpi_userdata/Music/fourdee_tech.ogg' ]] && return
		G_EXEC curl -sSfL https://dietpi.com/downloads/audio/fourdee_tech.ogg -o /mnt/dietpi_userdata/Music/fourdee_tech.ogg
		G_EXEC chown dietpi:dietpi /mnt/dietpi_userdata/Music/fourdee_tech.ogg
		G_EXEC chmod 664 /mnt/dietpi_userdata/Music/fourdee_tech.ogg

	}

	# Return optimisation values for BitTorrent servers based on device and hardware capabilities.
	Optimise_BitTorrent(){

		local output=0

		local gigabit_device=1
		(( $G_HW_MODEL < 4 || $G_HW_MODEL == 30 || $G_HW_MODEL == 32 || $G_HW_MODEL == 40 || $G_HW_MODEL == 60 || $G_HW_MODEL == 70 )) && gigabit_device=0

		# Cache size (MB) 1/10th of total mem
		if (( $1 == 0 )); then

			output=$(( $RAM_PHYS / 10 ))

		# Max active downloads
		elif (( $1 == 1 )); then

			output=2

			# Bump up for x86
			(( $G_HW_ARCH == 10 )) && output=3

		# Max global connections
		elif (( $1 == 2 )); then

			output=20

			# Bump up for x86
			if (( $G_HW_ARCH == 10 )); then

				output=40

			# Gbit devices
			elif (( $gigabit_device )); then

				output=30

			# Reduce for RPi 1-3. This is due to the USB bus Ethernet in the ARM SoC, which cripples network throughput/performance/latency.
			# - RPi3
			elif (( $G_HW_MODEL == 3 )); then

				output=15

			# - RPi2
			elif (( $G_HW_MODEL == 2 )); then

				output=13

			# - RPi1 256/512
			elif (( $G_HW_MODEL < 2 )); then

				output=7

			fi

		# Max upload slots
		elif (( $1 == 3 )); then

			output=3

			# Bump up for x86
			if (( $G_HW_ARCH == 10 )); then

				output=5

			# Gbit devices
			elif (( $gigabit_device )); then

				output=4

			# - Reduce for RPi 1-3. This is due to the USB bus Ethernet in the ARM SoC, which cripples network throughput/performance/latency.
			elif (( $G_HW_MODEL < 4 )); then

				output=2

			fi

		fi

		echo $output

	}

	# Usage:
	#	Download_Install 'https://file.com/file' [/path/to/target]
	#	dps_index=$software_id Download_Install 'conf_0' /etc/conf.conf
	# Optional input variables:
	#	fallback_url='http...'	= URL to use if e.g. grabbing URL from api.github.com fails: https://dietpi.com/phpbb/viewtopic.php?p=17390#p17390
	#	no_check_url=[01]	= Optionally disable URL check
	#	dps_index=$software_id	= Download from DietPi GitHub repo based on software ID/index
	#	DEPS_LIST='pkg1 ...'	= Install APT dependency packages
	# NB: This does not support installs that require user input (eg: a whiptail prompt for deb installs)
	Download_Install(){

		# Verify input URL
		if [[ $1 ]]
		then
			local url=$1

		elif [[ $fallback_url ]]
		then
			G_DIETPI-NOTIFY 1 "Automatic latest ${aSOFTWARE_NAME[$software_id]} download URL detection failed."
			G_DIETPI-NOTIFY 1 "\"$fallback_url\" will be used as fallback, but a newer version might be available."
			G_DIETPI-NOTIFY 1 'Please report this at: https://github.com/MichaIng/DietPi/issues'
			local url=$fallback_url

		else
			G_DIETPI-NOTIFY 1 "An empty download URL was passed during ${aSOFTWARE_NAME[$software_id]} install. Please report this at: https://github.com/MichaIng/DietPi/issues"
			return 1
		fi

		local target=$2 # Target path
		local file=${url##*/} # Grab file name from URL
		local type=${file##*.} # Grab file type from file name to special handle deb|zip|tar(.gz|.bz2)|tgz|tbz2|7z

		# DietPi-Software conf/service mode
		# shellcheck disable=SC2154
		if disable_error=1 G_CHECK_VALIDINT "$dps_index" 0; then

			type='dps_index'
			url="https://raw.githubusercontent.com/$G_GITOWNER/DietPi/$G_GITBRANCH/.conf/dps_$dps_index/$url"

		fi

		# shellcheck disable=SC2154
		(( $no_check_url )) || G_CHECK_URL "$url"
		unset -v fallback_url dps_index no_check_url

		G_EXEC cd /tmp/$G_PROGRAM_NAME # Failsafe

		# Download file
		if [[ $DEPS_LIST ]]; then

			# Download as background thread if dependencies are to be installed
			G_THREAD_START curl -sSfL "$url" -o "$file"
			# shellcheck disable=SC2086
			G_AGI $DEPS_LIST
			DEPS_LIST=
			G_THREAD_WAIT

		else

			G_EXEC curl -sSfL "$url" -o "$file"

		fi

		# Process downloaded file
		if [[ $type == 'deb' ]]; then

			G_AGI "./$file"

		elif [[ $type == 'zip' ]]; then

			G_EXEC unzip -o "$file" ${target:+"-d$target"}

		elif [[ $type =~ ^(t?gz|t?bz2|tar)$ ]]; then

			G_EXEC tar xf "$file" ${target:+"--one-top-level=$target"}

		elif [[ $type == '7z' ]]; then

			G_EXEC 7zr x -y "$file" ${target:+"-o$target"}

		elif [[ $target && $target != "$file" ]]; then

			# Pre-create target dir, if given
			[[ $target == *'/'* && ! -d ${target%/*} ]] && G_EXEC mkdir -p "${target%/*}"

			[[ -f $target ]] && G_DIETPI-NOTIFY 2 "Updating file: $target"
			G_EXEC mv "$file" "$target"

		else

			return

		fi

		[[ -f $file ]] && G_EXEC rm "$file"

	}

	Create_User()
	{
		# Parse input
		local group_primary groups_supplementary home shell password user
		while [[ $1 ]]
		do
			# Mark as read-only to throw an error on doubled input
			case $1 in
				-g) shift; readonly group_primary=$1; shift;; # Primary group is always pre-created and defaults to same name as user for new users
				-G) shift; readonly groups_supplementary=$1; shift;; # User is only added to supplementary groups that do exist
				-d) shift; readonly home=$1; shift;; # Home is never pre-created and defaults to "/nonexistent" which prevents login
				-s) shift; readonly shell=$1; shift;; # Shell defaults to "nologin" which prevents shell access on login even if home exists
				-p) shift; readonly password=$1; shift;; # Password is only set for new users while existing users passwords stay untouched
				*) readonly user=$1; shift;;
			esac
		done

		# Validate input
		# - User name needs to be given
		G_EXEC_DESC="Verifying user \"${user:-<missing!>}\" to run ${aSOFTWARE_NAME[$software_id]}" G_EXEC test "$user"
		# - Pre-create given primary group as useradd and usermod fail if it does not exist
		[[ $group_primary ]] && ! getent group "$group_primary" > /dev/null && G_EXEC groupadd -r "$group_primary"
		# - Only add to supplementary groups that do exist
		local group groups
		for group in ${groups_supplementary//,/ }
		do
			getent group "$group" > /dev/null && groups+=",$group"
		done
		groups=${groups#,}

		if getent passwd "$user" > /dev/null
		then
			G_EXEC usermod ${group_primary:+-g "$group_primary"} ${groups:+-aG "$groups"} -d "${home:-/nonexistent}" -s "${shell:-$(command -v nologin)}" "$user"
		else
			local options='-rMU'
			[[ $group_primary ]] && options='-rMN'
			G_EXEC useradd "$options" ${group_primary:+-g "$group_primary"} ${groups:+-G "$groups"} -d "${home:-/nonexistent}" -s "${shell:-$(command -v nologin)}" "$user"
			[[ $password ]] && G_EXEC_DESC="Applying user password: \e[33m${password//?/*}\e[0m" G_EXEC eval "chpasswd <<< '$user:$password'"
		fi
	}

	# Start a service and wait for a default config file to be created.
	Create_Config()
	{
		local file=$1 service=$2
		local timeout=${3:-25} # Optional
		local output=$4 pid # Optional, if timeout is set, hence for startups which may take long so that showing some process becomes reasonable
		local content=$CREATE_CONFIG_CONTENT # Optional
		unset -v CREATE_CONFIG_CONTENT

		# If file exists already and contains required content, continue install
		[[ -f $file ]] && { [[ ! $content ]] || grep -q "$content" "$file"; } && return 0

		# Reload services
		G_EXEC systemctl daemon-reload

		# File does not exist or does not contain required content: Start service
		G_EXEC_DESC="Starting ${aSOFTWARE_NAME[$software_id]} to pre-create config file in max $timeout seconds" G_EXEC systemctl start "$service"

		# Print and follow output of the service startup
		[[ $output ]] && { journalctl -fu "$service" & pid=$!; }

		# Wait for max $timeout seconds until config file has been created and required content added, if given
		local i=0
		until [[ -f $file ]] && { [[ ! $content ]] || grep -q "$content" "$file"; } || (( $i >= $timeout )) || ! systemctl -q is-active "$service"
		do
			((i++))
			[[ $output ]] || G_DIETPI-NOTIFY -2 "Waiting for ${aSOFTWARE_NAME[$software_id]} config file to be created ($i/$timeout)"
			sleep 1
		done

		# Stop journal prints
		[[ $output ]] && { kill $pid; wait $pid; } 2> /dev/null

		# Stop service
		sleep 1
		G_EXEC_NOHALT=1 G_EXEC systemctl stop "$service"
		sleep 1

		# If file exists already and contains required content, continue install
		if [[ -f $file ]] && { [[ ! $content ]] || grep -q "$content" "$file"; }
		then
			G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} config file got created after $i seconds"
			return 0
		fi

		G_DIETPI-NOTIFY 1 "Waiting for ${aSOFTWARE_NAME[$software_id]} config file failed, skipping pre-configuration"
		return 1
	}

	# Run marked software installs
	Install_Dietpi_Software(){

		local software_id

		software_id=23 # LXDE
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			# RPi: Block packages from RPi desktop which conflict with a native LXDE desktop: https://github.com/MichaIng/DietPi/issues/1558#issuecomment-691206284
			if (( $G_HW_MODEL < 10 )); then

				cat << '_EOF_' > /etc/apt/preferences.d/dietpi-lxde
Package: pcmanfm libfm4 libfm-gtk4 lxpanel lxpanel-data
Pin: origin archive.raspberrypi.org
Pin-Priority: -1
_EOF_
			fi
			# https://github.com/MichaIng/DietPi/issues/1558#issuecomment-701547904
			local dbus_package=
			(( ${aSOFTWARE_INSTALL_STATE[28]} > 0 )) && dbus_package='dbus-user-session'
			G_AGI lxde upower firefox-esr $dbus_package xcompmgr

		fi

		software_id=173 # LXQt
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Buster: No leafpad available, use featherpad instead: https://github.com/MichaIng/DietPi/issues/1918#issuecomment-489319719
			local editor='leafpad'
			(( $G_DISTRO > 4 )) && editor='featherpad'

			G_AGI lxqt qterminal firefox-esr xarchiver lxde-icon-theme upower xscreensaver $editor speedcrunch

		fi

		software_id=24 # MATE
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI mate-desktop-environment-core mate-media upower firefox-esr

		fi

		software_id=26 # GNUstep
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI wmaker gnustep gnustep-devel gnustep-games upower policykit-1 firefox-esr xterm

		fi

		software_id=25 # Xfce
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI xfce4 xfce4-terminal gnome-icon-theme tango-icon-theme upower policykit-1 firefox-esr

		fi

		software_id=22 # QuiteRSS
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI quiterss

		fi

		software_id=174 # GIMP
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI gimp

		fi

		software_id=175 # Xfce Power Manager
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI xfce4-power-manager

		fi

		software_id=29 # XRDP
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			# On Stretch, use backports, which resolves missing /etc/profile (e.g. $PATH) load and failing install+service if IPv6 is disabled: https://github.com/MichaIng/DietPi/issues/3017
			[[ $G_DISTRO == 4 && $G_RASPBIAN != 1 ]] && cat << '_EOF_' > /etc/apt/preferences.d/dietpi-xrdp
Package: xrdp xorgxrdp
Pin: release n=stretch-backports
Pin-Priority: 500
_EOF_
			G_AGI xrdp xorgxrdp

			# Workaround for failing mouse and keyboard input: https://github.com/MichaIng/DietPi/issues/3022
			G_CONFIG_INJECT 'Option "CoreKeyboard"' '    Option "CoreKeyboard"' /etc/X11/xrdp/xorg.conf 'Driver "xrdpkeyb"'
			G_CONFIG_INJECT 'Option "CorePointer"' '    Option "CorePointer"' /etc/X11/xrdp/xorg.conf 'Driver "xrdpmouse"'

			G_EXEC systemctl enable xrdp
			aSTART_SERVICES+=('xrdp')

		fi

		software_id=30 # NoMachine
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			Download_Install "https://dietpi.com/downloads/binaries/all/nomachine_$G_HW_ARCH_NAME.deb"
			G_EXEC systemctl enable nxserver
			aSTART_SERVICES+=('nxserver')

		fi

		software_id=44 # Transmission
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI transmission-daemon

		fi

		software_id=94 # ProFTPD
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			debconf-set-selections <<< 'proftpd-basic shared/proftpd/inetd_or_standalone select standalone'
			G_AGI proftpd-basic

		fi

		software_id=96 # Samba Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Link disk cache to RAM: https://github.com/MichaIng/DietPi/issues/2396
			# - Remove previous disk cache dir or symlink
			G_EXEC rm -Rf /var/cache/samba
			# - Pre-create RAM cache dir
			G_EXEC mkdir -p /run/samba-cache
			# - Link disk cache to RAM
			G_EXEC ln -s /run/samba-cache /var/cache/samba
			# - Create RAM cache dir automatically on boot
			echo 'd /run/samba-cache' > /etc/tmpfiles.d/dietpi-samba_cache.conf

			G_AGI samba

		fi

		software_id=95 # vsftpd
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI vsftpd

		fi

		software_id=109 # NFS Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI nfs-kernel-server

		fi

		software_id=184 # Tor Relay
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI tor

		fi

		software_id=83 # Apache
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			local apackages=('apache2')
			# Install certbot module, if certbot was already installed
			(( ${aSOFTWARE_INSTALL_STATE[92]} == 2 )) && apackages+=('python3-certbot-apache')
			G_AGI "${apackages[@]}"

		fi

		software_id=85 # Nginx
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			local apackages=('nginx-light')
			# Install certbot module, if certbot was already installed
			(( ${aSOFTWARE_INSTALL_STATE[92]} == 2 )) && apackages+=('python3-certbot-nginx')
			G_AGI "${apackages[@]}"

		fi

		software_id=84 # Lighttpd
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			# perl is required for lighty-enable-mod, it has been degraded to recommends only with Buster.
			G_AGI lighttpd perl

		fi

		software_id=88 # MariaDB
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			G_DIETPI-NOTIFY 2 'Preparing database directory at: /mnt/dietpi_userdata/mysql'
			if [[ -d '/mnt/dietpi_userdata/mysql' ]]; then

				G_DIETPI-NOTIFY 2 '/mnt/dietpi_userdata/mysql exists, will migrate containing databases'

			else
				# Otherwise use possibly existent /var/lib/mysql
				# - Remove possible dead symlinks/files:
				G_EXEC rm -f /mnt/dietpi_userdata/mysql
				if [[ -d '/var/lib/mysql' ]]; then

					G_DIETPI-NOTIFY 2 '/var/lib/mysql exists, will migrate containing databases'
					# Failsafe: Move symlink target in case, otherwise readlink will resolve to dir
					G_EXEC mv "$(readlink -f '/var/lib/mysql')" /mnt/dietpi_userdata/mysql

				else

					G_EXEC mkdir -p /mnt/dietpi_userdata/mysql

				fi

			fi

			G_EXEC rm -Rf /var/lib/mysql
			G_EXEC ln -s /mnt/dietpi_userdata/mysql /var/lib/mysql

			local apackages=('mariadb-server')
			# Install PHP module, if PHP was already installed
			(( ${aSOFTWARE_INSTALL_STATE[89]} == 2 )) && apackages+=("$PHP_NAME-mysql")
			G_AGI "${apackages[@]}"

			# Remove obsolete sysvinit service as we use the systemd mariadb.service
			if [[ -f '/etc/init.d/mysql' ]]; then

				G_DIETPI-NOTIFY 2 'Switching from /etc/init.d/mysql to mariadb.service'
				systemctl disable --now mysql
				rm /etc/init.d/mysql
				update-rc.d -f mysql remove

			fi

		fi

		software_id=87 # SQLite
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			local apackages=('sqlite3')
			# Install PHP module, if PHP was already installed
			(( ${aSOFTWARE_INSTALL_STATE[89]} == 2 )) && apackages+=("$PHP_NAME-sqlite3")
			G_AGI "${apackages[@]}"

		fi

		software_id=91 # Redis
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			local apackages=('redis-server')
			# Install PHP module, if PHP was already installed
			(( ${aSOFTWARE_INSTALL_STATE[89]} == 2 )) && apackages+=("$PHP_NAME-redis")
			G_AGI "${apackages[@]}"

		fi

		software_id=89 # PHP
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Stretch: Add Ondrejs PHP repository for latest PHP version (currently PHP7.3)
			if (( $G_DISTRO < 5 )); then

				# Debian (+sury.org) armhf is not ARMv6 compatible: https://github.com/MichaIng/DietPi/issues/2794
				if (( $G_HW_ARCH < 2 )); then

					INSTALL_URL_ADDRESS='http://raspbian.raspberrypi.org/raspbian/'
					# Actually we do not support any non-RPi ARMv6 devices currently, but lets be failsafe here
					(( $G_HW_MODEL > 9 )) && INSTALL_URL_ADDRESS='https://deb.debian.org/debian/'

					# APT source
					echo "deb $INSTALL_URL_ADDRESS buster main" > /etc/apt/sources.list.d/dietpi-php.list
					# Set priority for Buster: No auto install but auto upgrade
					echo -e '# Allow to install PHP7.3 dependencies and meta packages from Buster
# - Lighttpd must be pulled as well from Buster since the Stretch version does not support Buster libssl1.1 (1.1.1)
Package: php libapache2-mod-php php-* libssl* libc6* libc-* libgssapi-krb5-2 libpcre2-8-0 libk5crypto3 libkrb5-3 libkrb5support0 locales libzip4 curl libcurl4-openssl-dev lighttpd
Pin: release n=buster\nPin-Priority: 501\n
# Pin down all other Buster packages to only allow upgrades of already installed ones via: "apt upgrade"
Package: *\nPin: release n=buster\nPin-Priority: 100' > /etc/apt/preferences.d/dietpi-php

				else

					# Check URL
					INSTALL_URL_ADDRESS='https://packages.sury.org/php/apt.gpg'
					G_CHECK_URL "$INSTALL_URL_ADDRESS"
					# APT key
					curl -sSfL "$INSTALL_URL_ADDRESS" | apt-key add -
					# APT source
					echo "deb https://packages.sury.org/php/ $G_DISTRO_NAME main" > /etc/apt/sources.list.d/dietpi-php.list
					# Pin down libssl1.1 version from this repo on Stretch: https://github.com/MichaIng/DietPi/issues/2734
					echo -e '# libssl1.1 from sury.org breaks Lighttpd install
Package: openssl libssl*\nPin: origin packages.sury.org\nPin-Priority: -1' > /etc/apt/preferences.d/dietpi-openssl

				fi

				# APT list update
				G_AGUP

			fi

			# Base PHP modules
			# - Apache: mod_php
			if (( ${aSOFTWARE_INSTALL_STATE[83]} > 0 )); then

				local apackages=("libapache2-mod-$PHP_NAME")

			# - Lighttpd and Nginx: PHP-FPM
			elif (( ${aSOFTWARE_INSTALL_STATE[84]} > 0 || ${aSOFTWARE_INSTALL_STATE[85]} > 0 )); then

				local apackages=("$PHP_NAME-fpm")

			# - No webserver: CLI usage only (php binary)
			else

				local apackages=("$PHP_NAME-cli")

			fi

			# Additional PHP modules, commonly used by most web applications
			apackages+=("$PHP_NAME-apcu" "$PHP_NAME-curl" "$PHP_NAME-gd" "$PHP_NAME-mbstring" "$PHP_NAME-xml" "$PHP_NAME-zip")

			# MySQL/MariaDB PHP module
			(( ${aSOFTWARE_INSTALL_STATE[88]} > 0 )) && apackages+=("$PHP_NAME-mysql")

			# SQLite PHP module
			(( ${aSOFTWARE_INSTALL_STATE[87]} > 0 )) && apackages+=("$PHP_NAME-sqlite3")

			# Redis PHP module
			(( ${aSOFTWARE_INSTALL_STATE[91]} > 0 )) && apackages+=("$PHP_NAME-redis")

			G_AGI "${apackages[@]}"

		fi

		software_id=90 # phpMyAdmin
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Install required PHP modules: https://docs.phpmyadmin.net/en/latest/require.html#php
			G_AGI $PHP_NAME-{curl,gd,json,mbstring,xml,zip}

			# Quick install: https://docs.phpmyadmin.net/en/latest/setup.html#quick-install
			# - Get latest version name
			local version=$(curl -sSfL 'https://api.github.com/repos/phpmyadmin/phpmyadmin/releases/latest' | mawk -F\" '/"name": /{print $4}')
			[[ $version ]] || { version='5.0.4'; G_DIETPI-NOTIFY 1 "Automatic latest ${aSOFTWARE_NAME[$software_id]} version detection failed. Version \"$version\" will be installed as fallback, but a newer version might be available. Please report this at: https://github.com/MichaIng/DietPi/issues"; }
			Download_Install "https://files.phpmyadmin.net/phpMyAdmin/$version/phpMyAdmin-$version-english.tar.gz"
			# - Reinstall: Clean install but preserve existing config file
			[[ -f '/var/www/phpmyadmin/config.inc.php' ]] && G_EXEC mv /var/www/phpmyadmin/config.inc.php "phpMyAdmin-$version-english/"
			G_EXEC rm -Rf /var/www/phpmyadmin # Include pre-v6.27 symlink: https://github.com/MichaIng/DietPi/issues/3304
			# - Remove GUI setup: https://docs.phpmyadmin.net/en/latest/setup.html#securing-your-phpmyadmin-installation
			G_EXEC_NOHALT=1 G_EXEC rm -R "phpMyAdmin-$version-english/setup"
			# - Move new instance in place
			G_EXEC mv "phpMyAdmin-$version-english" /var/www/phpmyadmin

		fi

		software_id=128 # MPD
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Stretch
			if (( $G_DISTRO < 5 )); then

				DEPS_LIST='libnfs8 libsmbclient libsqlite3-0 libupnp6 libwrap0 libmpdclient2 libflac8 libyajl2 libavahi-client3 libvorbisfile3 libwavpack1 libmad0 libmpg123-0 libopus0 libavformat57 libfaad2 libcdio-paranoia1 libiso9660-8 libshout3 libid3tag0'
				Download_Install "https://dietpi.com/downloads/binaries/all/mpd_0.20.23-1_$G_HW_ARCH_NAME.deb"

			# Buster+
			else

				# libcdio-paranoia1, libiso9660-8 and libupnp6 not available, but ibcdio-paranoia2, libiso9660-11 and libupnp13 instead
				# Buster repo version >= 0.21.5, thus stay with APT for now
				G_AGI mpd

			fi

		fi

		software_id=54 # phpBB
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Support old and new location
			PHPBB_LOC='phpbb'
			[[ -d '/var/www/phpBB3' ]] && PHPBB_LOC='phpBB3'

			# Skip install, if already present
			if [[ -d /var/www/$PHPBB_LOC ]]; then

				G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} install dir \"/var/www/$PHPBB_LOC\" already exists. Download and install steps will be skipped.
 - If you want to update ${aSOFTWARE_NAME[$software_id]}, please follow the instructions from WebUI ACP.
 - if you need to reinstall (e.g. broken instance), please manually backup your config files+data, remove the install dir and rerun \"dietpi-software (re)install $software_id\"."

			else

				Download_Install 'https://download.phpbb.com/pub/release/3.3/3.3.3/phpBB-3.3.3.tar.bz2'
				G_EXEC mv phpBB3 /var/www/phpbb
				# Files are shipped with strange UID:GID 1000:1000 while for security reasons it should be root:root.
				G_EXEC chown -R root:root /var/www/phpbb

			fi

		fi

		software_id=58 # OpenBazaar
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Install Golang Go: https://github.com/OpenBazaar/openbazaar-go/blob/master/docs/install-linux.md#install-go
			# 	https://golang.org/dl/
			local version='1.15.3'
			# - x86_64
			if (( $G_HW_ARCH == 10 )); then

				INSTALL_URL_ADDRESS="https://dl.google.com/go/go$version.linux-amd64.tar.gz"

			# - ARMv8
			elif (( $G_HW_ARCH == 3 )); then

				INSTALL_URL_ADDRESS="https://dl.google.com/go/go$version.linux-arm64.tar.gz"

			# - ARMv6/7
			else

				INSTALL_URL_ADDRESS="https://dl.google.com/go/go$version.linux-armv6l.tar.gz"

			fi

			Download_Install "$INSTALL_URL_ADDRESS" /usr/local/

			# Export Go path variables
			G_EXEC mkdir -p /mnt/dietpi_userdata/go
			cat << '_EOF_' > /etc/bashrc.d/go.sh
#!/bin/dash
export GOPATH=/mnt/dietpi_userdata/go
export PATH=$PATH:/usr/local/go/bin:/mnt/dietpi_userdata/go/bin
_EOF_
			. /etc/bashrc.d/go.sh

			# Install OpenBazaar
			G_DIETPI-NOTIFY 2 "Installing ${aSOFTWARE_NAME[$software_id]}, please wait, this will take some time (1-5 minutes)"
			G_EXEC go get -u github.com/OpenBazaar/openbazaar-go

		fi

		software_id=133 # YaCy
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			Download_Install 'http://latest.yacy.net/yacy_v1.922_20191013_9964.tar.gz' /etc

		fi

		software_id=2 # Folding@Home
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			G_DIETPI-NOTIFY 2 'Pre-configuring FAHClient DEB package'
			debconf-set-selections <<< 'fahclient fahclient/autostart boolean false' # Do not start SysV service after package install
			debconf-set-selections <<< 'fahclient fahclient/power select light'
			debconf-set-selections <<< 'fahclient fahclient/team string 234437' # Team "DietPi"
			debconf-set-selections <<< 'fahclient fahclient/user string DietPi' # User "DietPi"
			debconf-set-selections <<< 'fahclient fahclient/passkey string 06c869246e88c00cb05cc4d1758a97f9' # Passkey for user "DietPi"

			# ARMv8
			if (( $G_HW_ARCH == 3 ))
			then
				Download_Install 'https://download.foldingathome.org/releases/public/release/fahclient/debian-stable-arm64/v7.6/latest.deb'
			# x86_64
			else
				Download_Install 'https://download.foldingathome.org/releases/public/release/fahclient/debian-stable-64bit/v7.6/latest.deb'
			fi

		fi

		software_id=47 # ownCloud
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			DEPS_LIST="$PHP_NAME-intl" # https://doc.owncloud.org/server/administration_manual/installation/manual_installation.html#php-extensions

			if [[ -f '/var/www/owncloud/occ' ]]; then

				G_DIETPI-NOTIFY 2 'Existing ownCloud installation found, will NOT overwrite...'

			else

				local datadir=$(sed -n '/^[[:blank:]]*SOFTWARE_OWNCLOUD_DATADIR=/{s/^[^=]*=//p;q}' /boot/dietpi.txt | sed 's|/$||')
				[[ $datadir ]] || datadir='/mnt/dietpi_userdata/owncloud_data'
				if [[ -f $datadir/dietpi-owncloud-installation-backup/occ ]]; then

					G_DIETPI-NOTIFY 2 'ownCloud installation backup found, starting recovery...'
					G_EXEC cp -a "$datadir"/dietpi-owncloud-installation-backup/. /var/www/owncloud/
					# Correct config.php data directory entry, in case it changed due to server migration:
					G_CONFIG_INJECT "'datadirectory'" "'datadirectory' => '$datadir'," /var/www/owncloud/config/config.php "'dbtype'"

				else

					Download_Install 'https://download.owncloud.org/community/owncloud-latest.tar.bz2' /var/www

				fi

			fi

			chown -R www-data:www-data /var/www/owncloud

			[[ $DEPS_LIST ]] && G_DIETPI-NOTIFY 2 'Installing required PHP modules' && G_AGI $DEPS_LIST
			DEPS_LIST=

		fi

		software_id=114 # Nextcloud
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			DEPS_LIST="$PHP_NAME-intl" # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation

			if [[ -f '/var/www/nextcloud/occ' ]]; then

				G_DIETPI-NOTIFY 2 'Existing Nextcloud installation found, will NOT overwrite...'

			else

				local datadir=$(sed -n '/^[[:blank:]]*SOFTWARE_NEXTCLOUD_DATADIR=/{s/^[^=]*=//p;q}' /boot/dietpi.txt | sed 's|/$||')
				[[ $datadir ]] || datadir='/mnt/dietpi_userdata/nextcloud_data'
				if [[ -f $datadir/dietpi-nextcloud-installation-backup/occ ]]; then

					G_DIETPI-NOTIFY 2 'Nextcloud installation backup found, starting recovery...'
					G_EXEC cp -a "$datadir"/dietpi-nextcloud-installation-backup/. /var/www/nextcloud/
					# Correct config.php data directory entry, in case it changed due to server migration:
					G_CONFIG_INJECT "'datadirectory'" "'datadirectory' => '$datadir'," /var/www/nextcloud/config/config.php "'dbtype'"

				else

					Download_Install 'https://download.nextcloud.com/server/releases/latest.tar.bz2' /var/www

				fi

			fi

			chown -R www-data:www-data /var/www/nextcloud

			[[ $DEPS_LIST ]] && G_DIETPI-NOTIFY 2 'Installing required PHP modules' && G_AGI $DEPS_LIST
			DEPS_LIST=

		fi

		software_id=168 # Nextcloud Talk
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Install Coturn server only, install Nextcloud Talk app after Nextcloud has been fully configured
			G_AGI coturn

		fi

		software_id=32 # ympd
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			Download_Install 'https://dietpi.com/downloads/binaries/all/ympd_1.2.3.7z'

			local binary_name='ympd_'
			# ARMv6
			if (( $G_HW_ARCH == 1 )); then

				binary_name+='armv6'

			# ARMv7
			elif (( $G_HW_ARCH == 2 )); then

				binary_name+='armv7'

			# ARMv8
			elif (( $G_HW_ARCH == 3 )); then

				binary_name+='armv8'

			# x86_64
			elif (( $G_HW_ARCH == 10 )); then

				binary_name+='amd64'

			fi

			# We have no Buster binaries yet, so always use Stretch binaries
			binary_name+='_stretch'

			G_EXEC mv $binary_name /usr/bin/ympd
			G_EXEC chmod +x /usr/bin/ympd
			G_EXEC_NOEXIT=1 G_EXEC rm ympd_*

		fi

		software_id=148 # myMPD
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Download source
			Download_Install 'https://github.com/jcorporation/myMPD/archive/master.tar.gz'
			# Install dependencies, build and install myMPD
			G_EXEC cd myMPD-master
			G_EXEC ./build.sh installdeps
			G_EXEC ./build.sh releaseinstall
			# Cleanup
			G_EXEC cd /tmp/$G_PROGRAM_NAME
			G_EXEC_NOEXIT=1 G_EXEC rm -R myMPD-master

		fi

		software_id=121 # Roon Bridge
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# x86_64
			if (( $G_HW_ARCH == 10 )); then

				INSTALL_URL_ADDRESS='https://download.roonlabs.com/builds/RoonBridge_linuxx64.tar.bz2'

			# ARMv8
			elif (( $G_HW_ARCH == 3 )); then

				INSTALL_URL_ADDRESS='https://download.roonlabs.com/builds/RoonBridge_linuxarmv8.tar.bz2'

			# ARMv7
			else

				INSTALL_URL_ADDRESS='https://download.roonlabs.com/builds/RoonBridge_linuxarmv7hf.tar.bz2'

			fi

			Download_Install "$INSTALL_URL_ADDRESS"

			# Clear dir on reinstall
			[[ -d '/etc/roonbridge' ]] && G_EXEC rm -R /etc/roonbridge
			G_EXEC mv RoonBridge /etc/roonbridge

		fi

		software_id=119 # CAVA
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# ARMv6
			if (( $G_HW_ARCH == 1 )); then

				INSTALL_URL_ADDRESS='https://dietpi.com/downloads/binaries/all/cava_0.6.1-1_armv6.deb'

			# ARMv7
			elif (( $G_HW_ARCH == 2 )); then

				INSTALL_URL_ADDRESS='https://dietpi.com/downloads/binaries/all/cava_0.6.1-1_armv7.deb'

			# ARMv8
			elif (( $G_HW_ARCH == 3 )); then

				INSTALL_URL_ADDRESS='https://dietpi.com/downloads/binaries/all/cava_0.6.1-1_arm64.deb'
				# Using older binary for Odroid C2 due to: https://github.com/MichaIng/DietPi/issues/1340#issuecomment-393225267
				(( $G_HW_MODEL == 12 )) && INSTALL_URL_ADDRESS='https://dietpi.com/downloads/binaries/all/cava_0.4.2_arm64.deb'

			# x86_64
			elif (( $G_HW_ARCH == 10 )); then

				INSTALL_URL_ADDRESS='https://dietpi.com/downloads/binaries/all/cava_0.6.1-1_amd64.deb'

			fi

			G_THREAD_START curl -sSfL https://dietpi.com/downloads/binaries/all/cava.psf -o /root/cava.psf # + Font for cava, nice bars

			DEPS_LIST='libpulse0 libfftw3-3 libncursesw5'
			Download_Install "$INSTALL_URL_ADDRESS"

		fi

		software_id=118 # Mopidy
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Install our config file only, if not yet existent, to preserve manual user config.
			# - This needs to be done prior to APT install, since this would otherwise install a default config file as well.
			[[ -f '/etc/mopidy/mopidy.conf' ]] || dps_index=$software_id Download_Install 'mopidy.conf' /etc/mopidy/mopidy.conf

			# Official repo does not support ARMv8 + Stretch, using distro repo instead: https://github.com/mopidy/apt/tree/master/dists/stretch/main
			if (( $G_HW_ARCH != 3 || $G_DISTRO > 4 )); then

				INSTALL_URL_ADDRESS='https://apt.mopidy.com/mopidy.gpg'
				G_CHECK_URL "$INSTALL_URL_ADDRESS"
				curl -sSfL "$INSTALL_URL_ADDRESS" | apt-key add -
				# No bullseye.list available yet, use buster.list instead: https://github.com/mopidy/apt/tree/master/dists
				G_EXEC curl -sSfL "https://apt.mopidy.com/${G_DISTRO_NAME/bullseye/buster}.list" -o /etc/apt/sources.list.d/mopidy.list
				G_AGUP

			fi

			# Stretch: Mopidy v2, pip2 and Mopidy-Local-Images
			if (( $G_DISTRO < 5 )); then

				G_AGI mopidy gstreamer1.0-alsa python-pip python-dev
				pip2 install -U pip setuptools wheel
				pip2 install -U Mopidy-MusicBox-Webclient Mopidy-Local-Images

			# Buster+: Mopidy v3, pip3 (ID=130) and mopidy-local: https://mopidy.com/ext/local/
			else

				G_AGI mopidy gstreamer1.0-alsa mopidy-local
				pip3 install -U Mopidy-MusicBox-Webclient

			fi

		fi

		software_id=31 # Kodi
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# CEC + NFS support
			if (( $G_DISTRO < 5 )); then

				apackages=('libcec4' 'libnfs8')

			elif (( $G_DISTRO == 5 )); then

				apackages=('libcec4' 'libnfs12')

			elif (( $G_DISTRO == 6 )); then

				apackages=('libcec6' 'libnfs13')

			fi

			# Odroids
			if [[ $G_HW_MODEL == 1[0-9] ]]; then

				# Odroid C2+N2+C4: aml-libs-odroid
				[[ $G_HW_MODEL == 1[256] ]] && apackages+=('aml-libs-odroid')

				# Odroid N1: librockchip-mpp1
				[[ $G_HW_MODEL == 14 ]] && apackages+=('librockchip-mpp1')

				# Odroid N2: https://github.com/MichaIng/DietPi/issues/3255
				if (( $G_HW_MODEL == 15 )); then

					apackages+=('malig52-fbdev-opencl-odroid' 'kodi-aml-fbdev-odroid')
					echo 'media_clock
firmware
decoder_common
stream_input
amvdec_avs
amvdec_h264
amvdec_h264_4k2k
amvdec_mh264
amvdec_h264mvc
amvdec_h265
amvdec_mjpeg
amvdec_mmjpeg
amvdec_mpeg12
amvdec_mpeg4
amvdec_mmpeg4
amvdec_real
amvdec_vc1
amvdec_vp9' > /etc/modules-load.d/dietpi-n2-kodi.conf

				# Odroid C4
				elif (( $G_HW_MODEL == 16 )); then

					apackages+=('malig31-fbdev-opencl-odroid' 'kodi-aml-fbdev-odroid')
					echo 'media_clock
firmware
decoder_common
stream_input
amvdec_avs
amvdec_h264
amvdec_h264_4k2k
amvdec_mh264
amvdec_h264mvc
amvdec_h265
amvdec_mjpeg
amvdec_mmjpeg
amvdec_mpeg12
amvdec_mpeg4
amvdec_mmpeg4
amvdec_real
amvdec_vc1
amvdec_vp9' > /etc/modules-load.d/dietpi-c4-kodi.conf

				else

					# libcurl3-gnutls missing as dependency for kodi-odroid on arm64 Stretch (Odroid C2): https://github.com/MichaIng/DietPi/issues/446
					(( $G_HW_ARCH == 3 && $G_DISTRO < 5 )) && apackages+=('libcurl3-gnutls')
					apackages+=('kodi-odroid')

				fi

			# Everything else
			else

				apackages+=('kodi')

				# RPi
				if (( $G_HW_MODEL < 10 )); then

					# fbset required to recover desktop with custom resolution and fix startup warning: https://dietpi.com/phpbb/viewtopic.php?p=17550#p17550
					apackages+=('fbset')

					# RPi4: fake KMS driver required: https://www.raspberrypi.org/forums/viewtopic.php?f=66&t=251645
					if (( $G_HW_MODEL == 4 )); then

						/boot/dietpi/func/dietpi-set_hardware rpi-opengl vc4-fkms-v3d

					# Else legacy (non-GL) driver required
					else

						/boot/dietpi/func/dietpi-set_hardware rpi-opengl disable

					fi

					# https://github.com/MichaIng/DietPi/issues/3031#issuecomment-540477241
					mkdir -p /etc/polkit-1/localauthority/50-local.d

					# https://github.com/MichaIng/DietPi/issues/3173
					local gpu_mem=$(sed -n '/^[[:blank:]]*gpu_mem_1024=/{s/^[^=]*=//p;q}' /boot/config.txt)
					disable_error=1 G_CHECK_VALIDINT "$gpu_mem" 128 944 || /boot/dietpi/func/dietpi-set_hardware gpumemsplit 128

				fi

			fi

			G_AGI "${apackages[@]}"

		fi

		software_id=39 # MiniDLNA
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI minidlna

		fi

		software_id=67 # No-IP
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# x86_x64
			if (( $G_HW_ARCH == 10 )); then

				INSTALL_URL_ADDRESS='https://dietpi.com/downloads/binaries/all/noip_x32_x64.zip'

			# ARMv8
			elif (( $G_HW_ARCH == 3 )); then

				INSTALL_URL_ADDRESS='https://dietpi.com/downloads/binaries/all/noip_arm64.zip'

			# ARMv6/7
			else

				INSTALL_URL_ADDRESS='https://dietpi.com/downloads/binaries/all/noip_armhf.zip'

			fi

			Download_Install "$INSTALL_URL_ADDRESS"

			G_EXEC mkdir -p /usr/local/bin
			G_EXEC mv noip_binary /usr/local/bin/noip2
			G_EXEC chmod +x /usr/local/bin/noip2

		fi

		software_id=108 # Amiberry
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Libraries
			# - net-tools: WHDLoad database update checkes for "route" command currently
			DEPS_LIST='libxml2 libfreetype6 libflac8 libmpeg2-4 libmpg123-0 net-tools' # libpng16-16 pulled by libfreetype6

			# Platform and GPU drivers
			# - RPi
			if (( $G_HW_MODEL < 10 )); then

				local platform='rpi1' # Include ID -1 + 0
				(( $G_HW_MODEL > 1 )) && platform="rpi$G_HW_MODEL"
				/boot/dietpi/func/dietpi-set_hardware rpi-opengl vc4-fkms-v3d

			# - Odroid XU4: https://dietpi.com/meveric/pool/main/s/setup-odroid/
			elif (( $G_HW_MODEL == 11 )); then

				local platform='xu4'
				DEPS_LIST+=' malit628-odroid'

			# - ASUS TB
			elif (( $G_HW_MODEL == 52 )); then

				local platform='RK3288'
				# Buster: Mali-T760 driver by RockChip: https://github.com/rockchip-linux/rk-rootfs-build/tree/master/packages/armhf
				if (( $G_DISTRO < 6 )); then

					Download_Install 'https://dietpi.com/downloads/binaries/asus/libmali.deb'
					G_EXEC cd /usr/lib/arm-linux-gnueabihf
					ln -sf libMali.so libEGL.so.1.1.0
					ln -sf libMali.so libEGL.so
					ln -sf libMali.so libEGL.so.1.0.0
					ln -sf libMali.so libEGL.so.1.4
					ln -sf libMali.so libEGL.so.1
					ln -sf libMali.so libGLESv2.so
					ln -sf libMali.so libGLESv2.so.2.0
					ln -sf libMali.so libGLESv2.so.2.0.0
					ln -sf libMali.so libGLESv1_CM.so
					ln -sf libMali.so libGLESv1_CM.so.1
					ln -sf libMali.so libGLESv1_CM.so.1.1
					G_EXEC cd /tmp/$G_PROGRAM_NAME

				# Bullseye: Use Mesa drivers provided by Debian repo
				else

					G_AGI libegl1 libgles2 libgl1-mesa-dri

				fi

			fi

			# Download Amiberry
			Download_Install "https://dietpi.com/downloads/binaries/$G_DISTRO_NAME/amiberry_$platform.tar.bz2"

			# Reinstall: Install new amiberry.conf as amiberry.conf_new, to preserve user settings
			[[ -f '/mnt/dietpi_userdata/amiberry/conf/amiberry.conf' ]] && G_EXEC mv amiberry/conf/amiberry.conf /mnt/dietpi_userdata/amiberry/conf/amiberry.conf_new

			# Move/Merge new instance in place
			G_EXEC cp -a amiberry /mnt/dietpi_userdata/

			# Cleanup
			G_EXEC_NOHALT=1 G_EXEC rm -R amiberry

		fi

		software_id=112 # DXX-Rebirth
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			DEPS_LIST='libsdl-mixer1.2 libsdl1.2debian libphysfs1'
			Download_Install 'https://dietpi.com/downloads/binaries/rpi/dxx-rebirth.7z' /mnt/dietpi_userdata

		fi

		software_id=111 # UrBackup Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Grab latest version
			INSTALL_URL_ADDRESS='https://hndl.urbackup.org/Server/latest'
			G_CHECK_URL "$INSTALL_URL_ADDRESS"

			# Latest known version
			local latest='2.4.13'

			# ARMv7
			if (( $G_HW_ARCH == 2 )); then

				local fallback_url="https://hndl.urbackup.org/Server/$latest/urbackup-server_${latest}_armhf.deb"
				local file=$(curl -sfL "$INSTALL_URL_ADDRESS" | grep -m1 'urbackup-server_.*_armhf\.deb' | cut -d \" -f 8)

			# x86_64
			elif (( $G_HW_ARCH == 10 )); then

				local fallback_url="https://hndl.urbackup.org/Server/$latest/urbackup-server_${latest}_amd64.deb"
				local file=$(curl -sfL "$INSTALL_URL_ADDRESS" | grep -m1 'urbackup-server_.*_amd64\.deb' | cut -d \" -f 8)

			# ARMv8
			elif (( $G_HW_ARCH == 3 )); then

				local fallback_url="https://hndl.urbackup.org/Server/$latest/urbackup-server_${latest}_arm64.deb"
				local file=$(curl -sfL "$INSTALL_URL_ADDRESS" | grep -m1 'urbackup-server-.*_arm64\.deb' | cut -d \" -f 8)

			fi

			[[ $file ]] && INSTALL_URL_ADDRESS="$INSTALL_URL_ADDRESS/$file" || INSTALL_URL_ADDRESS=$fallback_url
			unset file fallback_url

			debconf-set-selections <<< 'urbackup-server urbackup/backuppath string /mnt/dietpi_userdata/urbackup'
			Download_Install "$INSTALL_URL_ADDRESS"

		fi

		software_id=51 # OpenTyrian
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			DEPS_LIST='libsdl1.2debian libsdl-net1.2'
			Download_Install 'https://dietpi.com/downloads/binaries/rpi/opentyrian_armhf.zip' /

			# Move to /usr/games
			G_EXEC cp -a /usr/local/games/opentyrian /usr/games/
			G_EXEC rm -R /usr/local/games/opentyrian
			G_EXEC chmod +x /usr/games/opentyrian/opentyrian

		fi

		software_id=59 # RPi Cam Control
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			DEPS_LIST='gpac motion'
			Download_Install 'https://github.com/silvanmelchior/RPi_Cam_Web_Interface/archive/master.tar.gz'

			G_EXEC cd RPi_Cam_Web_Interface-master

			# Config /etc/motion
			G_EXEC mkdir -p /etc/motion
			G_EXEC cp etc/motion/motion.conf.1 /etc/motion/motion.conf

			# Config /etc/raspimjpeg
			G_EXEC cp etc/raspimjpeg/raspimjpeg.1 /etc/raspimjpeg

			# Setup /var/www/rpicam
			G_EXEC mkdir -p /var/www/rpicam
			G_EXEC cp -dR www/. /var/www/rpicam/
			G_EXEC mknod /var/www/rpicam/FIFO p
			G_EXEC mknod /var/www/rpicam/FIFO1 p

			# Symlink cam preview and status
			G_EXEC ln -sf /run/shm/mjpeg/cam.jpg /var/www/rpicam/cam.jpg
			G_EXEC ln -sf /run/shm/mjpeg/status_mjpeg.txt /var/www/rpicam/status_mjpeg.txt

			# Setup Raspimjpeg binary
			# - Use Stretch binary on Stretch
			(( $G_DISTRO < 5 )) && G_EXEC mv bin/raspimjpeg{-stretch,}
			G_EXEC cp bin/raspimjpeg /opt/vc/bin/raspimjpeg
			G_EXEC chmod +x /opt/vc/bin/raspimjpeg
			G_EXEC ln -sf /opt/vc/bin/raspimjpeg /usr/bin/raspimjpeg

			# Cleanup / remove extracted source
			G_EXEC cd /tmp/$G_PROGRAM_NAME
			G_EXEC_NOFAIL=1 G_EXEC rm -R RPi_Cam_Web_Interface-master

		fi

		software_id=45 # Deluge
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI deluged deluge-web deluge-console

		fi

		software_id=115 # Webmin
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			INSTALL_URL_ADDRESS='https://www.webmin.com/jcameron-key.asc'
			G_CHECK_URL "$INSTALL_URL_ADDRESS"
			G_EXEC eval "curl -sSLf '$INSTALL_URL_ADDRESS' | gpg --dearmor -o /etc/apt/trusted.gpg.d/dietpi-webmin.gpg --yes"
			echo 'deb https://download.webmin.com/download/repository/ sarge contrib' > /etc/apt/sources.list.d/webmin.list

			G_AGUP
			G_AGI webmin

		fi

		software_id=129 # O!MPD
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Enable YouTube and Tidal support: youtube-dl is available as standalone binary: https://yt-dl.org/download.html
			G_EXEC mkdir -p /usr/local/bin
			G_THREAD_START curl -sSfL 'https://yt-dl.org/downloads/latest/youtube-dl' -o /usr/local/bin/youtube-dl
			DEPS_LIST='python-requests'

			Download_Install 'https://github.com/ArturSierzant/OMPD/archive/master.tar.gz' /var/www
			# Replace existing installs but preserve local config override
			if [[ -d '/var/www/ompd' ]]; then

				[[ -f '/var/www/ompd/include/config.local.inc.php' ]] && G_EXEC mv /var/www/ompd/include/config.local.inc.php /var/www/OMPD-master/include/
				G_EXEC rm -R /var/www/ompd

			fi
			G_EXEC mv /var/www/OMPD-master /var/www/ompd

			G_EXEC chmod +x /usr/local/bin/youtube-dl

		fi

		software_id=135 # IceCast + DarkIce
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI darkice icecast2

		fi

		software_id=63 # LinuxDash
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			Download_Install 'https://github.com/afaqurk/linux-dash/archive/master.tar.gz'
			G_EXEC mkdir -p /var/www/linuxdash
			G_EXEC cp -a linux-dash-master/* /var/www/linuxdash/
			G_EXEC_NOHALT=1 G_EXEC rm -R linux-dash-master

		fi

		software_id=182 # Unbound
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 ))
		then
			Banner_Installing

			G_DIETPI-NOTIFY 2 'Pre-configuring Unbound to avoid port binding conflicts'

			# Download/update list of root hints
			Download_Install 'https://www.internic.net/domain/named.root' /var/lib/unbound/root.hints

			# Create monthly cron job to keep root hints updated
			echo -e '#!/bin/dash\n# Update Unbound root hints\ncurl -sSfL https://www.internic.net/domain/named.root -o /var/lib/unbound/root.hints' > /etc/cron.monthly/dietpi-unbound
			G_EXEC chmod +x /etc/cron.monthly/dietpi-unbound

			# Download base configuration if it does not exist yet
			[[ -f '/etc/unbound/unbound.conf.d/dietpi.conf' ]] || dps_index=$software_id Download_Install 'unbound.conf' /etc/unbound/unbound.conf.d/dietpi.conf

			# Toggle IPv6 preference based on dietpi.txt settings
			if grep -q 'CONFIG_ENABLE_IPV6=0' /boot/dietpi.txt
			then
				G_CONFIG_INJECT 'do-ip6:[[:blank:]]' '	do-ip6: no' /etc/unbound/unbound.conf.d/dietpi.conf

			elif grep -q 'CONFIG_PREFER_IPV4=0' /boot/dietpi.txt
			then
				G_CONFIG_INJECT 'prefer-ip6:[[:blank:]]' '	prefer-ip6: yes' /etc/unbound/unbound.conf.d/dietpi.conf
			fi

			# Stretch: Remove incompatile setting
			(( $G_DISTRO < 5 )) && sed -i '/log-replies/d' /etc/unbound/unbound.conf.d/dietpi.conf

			# Since IP binding might be used, start after network interfaces have been configured, not when they just start to be configured
			G_EXEC mkdir -p /etc/systemd/system/unbound.service.d
			G_EXEC eval "echo -e '[Unit]\nWants=network-online.target\nAfter=network-online.target' > /etc/systemd/system/unbound.service.d/dietpi.conf"

			# Pi-hole part 1
			if (( ${aSOFTWARE_INSTALL_STATE[93]} > 0 )) && grep -q '^[[:blank:]]*port:[[:blank:]][[:blank:]]*53$' /etc/unbound/unbound.conf.d/dietpi.conf
			then
				G_DIETPI-NOTIFY 2 'Configuring Unbound to work for Pi-hole'
				grep '^[[:blank:]]*nameserver[[:blank:]]' /etc/resolv.conf | grep -qvE '[[:blank:]]127.0.0.1(:53)?$' || echo 'nameserver 9.9.9.9' >> /etc/resolv.conf # Failsafe
				G_CONFIG_INJECT 'port:[[:blank:]]' '	port: 5335' /etc/unbound/unbound.conf.d/dietpi.conf
				G_CONFIG_INJECT 'interface:[[:blank:]]' '	interface: 127.0.0.1' /etc/unbound/unbound.conf.d/dietpi.conf
			fi

			[[ -f '/lib/systemd/system/unbound.service' ]] && G_EXEC systemctl restart unbound
			G_AGI unbound
			G_EXEC systemctl enable --now unbound # failsafe

			# Pi-hole part 2
			if (( ${aSOFTWARE_INSTALL_STATE[93]} == 2 ))
			then
				G_DIETPI-NOTIFY 2 'Configuring Pi-hole to use Unbound'
				if [[ -f '/etc/dnsmasq.d/01-pihole.conf' ]]
				then
					G_EXEC sed -i '/^[[:blank:]]*server=/d' /etc/dnsmasq.d/01-pihole.conf
					G_CONFIG_INJECT 'server=' 'server=127.0.0.1#5335' /etc/dnsmasq.d/01-pihole.conf
					systemctl -q is-active pihole-FTL && G_EXEC systemctl restart pihole-FTL
				fi
				if [[ -f '/etc/pihole/setupVars.conf' ]]
				then
					G_CONFIG_INJECT 'PIHOLE_DNS_1=' 'PIHOLE_DNS_1=127.0.0.1#5335' /etc/pihole/setupVars.conf
					G_EXEC sed -i '/^[[:blank:]]*PIHOLE_DNS_2=/d' /etc/pihole/setupVars.conf
				fi
			fi
		fi

		software_id=93 # Pi-hole
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 ))
		then
			Banner_Installing

			INSTALL_URL_ADDRESS='https://install.pi-hole.net'
			G_CHECK_URL "$INSTALL_URL_ADDRESS"

			# Check free available memory. Increase swap size to prevent gravity running out of mem.
			if (( $(free -m | mawk '/^Mem:/{print $7;exit}') < 512 && $(free -m | mawk '/^Swap:/{print $2;exit}') < 512 ))
			then
				G_DIETPI-NOTIFY 2 'Increasing swap size to 512 MiB for running gravity.sh, please wait...\n'
				/boot/dietpi/func/dietpi-set_swapfile 512
			fi

			# Dependencies: https://github.com/pi-hole/pi-hole/blob/development/automated%20install/basic-install.sh#L250
			G_AGI $PHP_NAME-xml $PHP_NAME-sqlite3 $PHP_NAME-intl

			# Unbound: Switch port to 5335 if it was installed before, else it got just configured within its install step above
			if (( ${aSOFTWARE_INSTALL_STATE[182]} == 2 )) && grep -q '^[[:blank:]]*port:[[:blank:]][[:blank:]]*53$' /etc/unbound/unbound.conf.d/dietpi.conf
			then
				G_WHIP_MSG 'You are installing Pi-hole while Unbound is already installed. The Unbound port will be changed to 5335.\n\nTo add it to Pi-hole, set "127.0.0.1#5335" as only custom (IPv4) upstream DNS server.'
				grep '^[[:blank:]]*nameserver[[:blank:]]' /etc/resolv.conf | grep -qvE '[[:blank:]]127.0.0.1(:53)?$' || echo 'nameserver 9.9.9.9' >> /etc/resolv.conf # Failsafe
				G_CONFIG_INJECT 'port:[[:blank:]]' '	port: 5335' /etc/unbound/unbound.conf.d/dietpi.conf
				G_CONFIG_INJECT 'interface:[[:blank:]]' '	interface: 127.0.0.1' /etc/unbound/unbound.conf.d/dietpi.conf
				G_EXEC systemctl restart unbound
			fi

			# Install
			G_EXEC curl -sSfL "$INSTALL_URL_ADDRESS" -o install.sh
			G_EXEC chmod +x install.sh
			# - Skip web server install, since we allow to choose and install it prior to Pi-hole
			# - Skip supported OS check. We do not support Debian testing suite but we are testing it already now.
			export PIHOLE_SKIP_OS_CHECK=true
			G_EXEC_NOEXIT=1 G_EXEC_OUTPUT=1 G_EXEC ./install.sh --disable-install-webserver || aSOFTWARE_INSTALL_STATE[$software_id]=0
			G_EXEC_NOHALT=1 G_EXEC rm install.sh
		fi

		software_id=33 # Airsonic
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			local fallback_url='https://github.com/airsonic/airsonic/releases/download/v10.6.2/airsonic.war'
			Download_Install "$(curl -sSfL 'https://api.github.com/repos/airsonic/airsonic/releases/latest' | mawk -F\" '/"browser_download_url": .*\/airsonic\.war"/{print $4}')" /mnt/dietpi_userdata/airsonic/airsonic.war

		fi

		software_id=34 # Subsonic
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			#DEPS_LIST='lame' # Conflicts with our ffmpeg package: https://github.com/MichaIng/DietPi/issues/946#issuecomment-300738228
			Download_Install 'https://dietpi.com/downloads/binaries/all/subsonic.deb'

		fi

		software_id=68 # Remot3.it
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# ARMv6/7
			local arch='armhf'

			# ARMv8
			if (( $G_HW_ARCH == 3 )); then

				arch='arm64'

			# x86_64
			elif (( $G_HW_ARCH == 10 )); then

				arch='amd64'

			fi

			local fallback_url="https://github.com/remoteit/installer/releases/download/v2.5.38/connectd_2.5.38_$arch.deb"
			Download_Install "$(curl -sSfL 'https://api.github.com/repos/remoteit/installer/releases/latest' | grep "\"browser_download_url\": .*/connectd_[^\"/]*_$arch\.deb\"" | cut -d \" -f 4)"

		fi

		# WebIOPi requires RPi GPIO
		(( ${aSOFTWARE_INSTALL_STATE[71]} == 1 )) && aSOFTWARE_INSTALL_STATE[69]=1

		software_id=69 # RPi GPIO
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI python-rpi.gpio python3-rpi.gpio

		fi

		software_id=70 # WiringPi
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			
			[[ -d '/usr/local/bin' ]] || G_EXEC mkdir -p /usr/local/bin

			# RPi
			if (( $G_HW_MODEL < 10 )); then

				# https://git.drogon.net/?p=wiringPi;a=shortlog;h=refs/heads/master snapshot
				Download_Install 'https://dietpi.com/downloads/binaries/all/wiringPi-8d188fa.tar.gz'

			# Odroids
			elif (( $G_HW_MODEL < 20 )); then

				Download_Install 'https://github.com/hardkernel/wiringPi/archive/master.tar.gz'

			# BPi Pro
			elif (( $G_HW_MODEL == 51 )); then

				Download_Install 'https://github.com/LeMaker/WiringBP/archive/bananapro.zip'

			fi

			mv WiringBP* wiringPi &> /dev/null # BPi
			mv wiringPi* wiringPi &> /dev/null # eg: RPi, wiringPi-HEAD-8d188fa

			G_EXEC cd wiringPi
			G_EXEC chmod +x build
			G_EXEC_OUTPUT=1 G_EXEC ./build
			G_EXEC cd /tmp/$G_PROGRAM_NAME

			G_EXEC rm -Rf /root/wiringPi
			G_EXEC mv wiringPi /root/

		fi

		software_id=72 # I2C
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			/boot/dietpi/func/dietpi-set_hardware i2c enable

		fi

		software_id=122 # Node-RED
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing # Pre-configure user and data directory to allow a local service user install
			
			# Data dir
			G_EXEC mkdir -p /mnt/dietpi_userdata/node-red

			# User
			Create_User -G gpio,i2c -d /mnt/dietpi_userdata/node-red nodered
			# - Allow sudo calls
			echo 'nodered ALL=NOPASSWD: ALL' > /etc/sudoers.d/nodered

			# Permissions
			G_EXEC chown -R nodered:nodered /mnt/dietpi_userdata/node-red

			# Service
			cat << '_EOF_' > /etc/systemd/system/node-red.service
[Unit]
Description=Node-RED (DietPi)

[Service]
User=nodered
ExecStart=/mnt/dietpi_userdata/node-red/node_modules/.bin/node-red -u /mnt/dietpi_userdata/node-red

[Install]
WantedBy=multi-user.target
_EOF_
			# Pre-reqs
			local apackages=('python3')
			# - RPi: GPIO control for Node-RED
			(( $G_HW_MODEL > 9 )) || apackages+=('python3-rpi.gpio')
			G_AGI "${apackages[@]}"

			# Install as local instance for "nodered" user
			G_EXEC cd /mnt/dietpi_userdata/node-red
			G_EXEC_OUTPUT=1 G_EXEC sudo -u nodered npm i node-red
			G_EXEC cd /tmp/$G_PROGRAM_NAME

			# CLI alias
			echo "alias node-red-admin='sudo -u nodered /mnt/dietpi_userdata/node-red/node_modules/.bin/node-red-admin'" > /etc/bashrc.d/dietpi-node-red.sh

		fi

		software_id=123 # Mosquitto
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Use official APT repository where available: https://repo.mosquitto.org/debian/pool/main/m/mosquitto/
			if [[ $G_HW_ARCH != 3 && $G_DISTRO -le 5 ]]; then

				INSTALL_URL_ADDRESS='https://repo.mosquitto.org/debian/mosquitto-repo.gpg.key'
				G_CHECK_URL "$INSTALL_URL_ADDRESS"
				G_EXEC eval "curl -sSLf '$INSTALL_URL_ADDRESS' | gpg --dearmor -o /etc/apt/trusted.gpg.d/dietpi-mosquitto.gpg --yes"
				G_EXEC eval "echo 'deb https://repo.mosquitto.org/debian/ $G_DISTRO_NAME main' > /etc/apt/sources.list.d/dietpi-mosquitto.list"
				G_AGUP

			fi

			G_AGI mosquitto

		fi

		software_id=131 # Blynk Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# On Stretch and ARMv6 (RPi 1/Zero) use Java 8, else most current
			local fallback_url='https://github.com/blynkkk/blynk-server/releases/download/v0.41.15/server-0.41.15.jar' java=
			(( $G_DISTRO < 5 || G_HW_ARCH == 1 )) && fallback_url='https://github.com/blynkkk/blynk-server/releases/download/v0.41.15/server-0.41.15-java8.jar' java='-java8'

			DEPS_LIST='python' Download_Install "$(curl -sSfL 'https://api.github.com/repos/blynkkk/blynk-server/releases/latest' | grep "\"browser_download_url\": .*/server-[0-9.]*$java\.jar\"" | cut -d \" -f 4)" /mnt/dietpi_userdata/blynk/blynkserver.jar

			# Install Blynk JS libary
			npm i -g --unsafe-perm onoff
			npm i -g --unsafe-perm blynk-library

		fi

		software_id=124 # NAA Daemon
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Base URL
			local url='https://www.signalyst.eu/bins/naa/linux'

			# Arch
			# - ARMv6/7
			local arch='armhf'

			# - ARMv8
			if (( $G_HW_ARCH == 3 )); then

				arch='arm64'

			# - x86_64
			elif (( $G_HW_ARCH == 10 )); then

				arch='amd64'

			fi

			# Get latest version: No Bullseye packages available yet
			local distro=$G_DISTRO_NAME
			(( $G_DISTRO > 5 )) && distro='buster'
			local package=$(curl -sSfL "$url/$distro/" | sed -n "/href=\"networkaudiod_[^\"]*_$arch.deb\"/{s/^[^\"]*\"//;s/\".*$//p}" | tail -1)

			# Check final URL before applying debconf settings
			G_CHECK_URL "$url/$distro/$package"

			# Skip license
			debconf-set-selections <<< 'networkaudiod networkaudiod/license note false'

			# Install
			no_check_url=1 Download_Install "$url/$distro/$package"

			unset url arch distro package

		fi

		software_id=125 # Tomcat8
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI tomcat8

		fi

		software_id=71 # WebIOPi
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			DEPS_LIST='python-dev python-setuptools'
			Download_Install 'https://sourceforge.net/projects/webiopi/files/WebIOPi-0.7.1.tar.gz/download' webiopi.tar.gz
			G_EXEC tar xf webiopi.tar.gz
			G_EXEC_NOHALT=1 G_EXEC rm webiopi.tar.gz

			G_EXEC cd WebIOPi-0.7.1

			# Automate Weaved prompt
			G_EXEC sed -i '/read response/c\response="n"' setup.sh

			# Run setup script
			G_EXEC_OUTPUT=1 G_EXEC ./setup.sh

			G_EXEC cd /tmp/$G_PROGRAM_NAME

			# Cleanup
			G_EXEC_NOHALT=1 G_EXEC rm -R WebIOPi-0.7.1

		fi

		software_id=98 # HAProxy
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			local version='2.3.2'
			DEPS_LIST='libpcre3-dev libssl-dev zlib1g-dev libsystemd-dev'
			Download_Install "https://www.haproxy.org/download/${version%.*}/src/haproxy-$version.tar.gz"

			# Compile
			G_EXEC cd haproxy-$version
			G_EXEC_OUTPUT=1 G_EXEC make -j "$G_HW_CPU_CORES" TARGET=linux-glibc CPU=generic USE_PCRE=1 USE_OPENSSL=1 USE_ZLIB=1 USE_SYSTEMD=1 ADDLIB='-Wl,--no-as-needed -lgcc_s -Wl,--as-needed' DEBUG_CFLAGS='-O3' LDFLAGS='-Wl,-z,relro -Wl,-z,now -Wl,--as-needed'
			# Strip binary size
			G_EXEC strip --remove-section=.comment --remove-section=.note haproxy
			# Install
			G_EXEC_OUTPUT=1 G_EXEC make install
			# - systemd unit
			G_EXEC cd contrib/systemd
			G_EXEC_OUTPUT=1 G_EXEC make
			G_EXEC mv {,/etc/systemd/system/}haproxy.service
			# - Error pages
			G_EXEC cd /tmp/$G_PROGRAM_NAME
			G_EXEC mkdir -p /etc/haproxy/errors
			G_EXEC mv haproxy-$version/examples/errorfiles/*.http /etc/haproxy/errors/
			# Clean up
			G_EXEC_NOHALT=1 G_EXEC rm -R haproxy-$version

		fi

		software_id=35 # Logitech Media Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing # https://wiki.slimdevices.com/index.php/DebianPackage#installing_7.9.2

			# Grab architecture
			local arch='arm'
			(( $G_HW_ARCH == 10 )) && arch='amd64'

			# Grab latest package URL: Force HTTPS!
			local fallback_url="https://downloads.slimdevices.com/LogitechMediaServer_v8.1.1/logitechmediaserver_8.1.1_$arch.deb"
			Download_Install "$(curl -sSfL "https://www.mysqueezebox.com/update/?version=8.1&geturl=1&os=deb$arch" | sed 's|^http://|https://|')"

		fi

		software_id=55 # Wordpress
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			Download_Install 'https://wordpress.org/latest.tar.gz' /var/www

		fi

		software_id=38 # FreshRSS
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Install required PHP modules: https://github.com/FreshRSS/FreshRSS#example-of-full-installation-on-linux-debianubuntu
			DEPS_LIST="$PHP_NAME-curl $PHP_NAME-gmp $PHP_NAME-intl $PHP_NAME-json $PHP_NAME-mbstring $PHP_NAME-xml $PHP_NAME-zip"

			Download_Install 'https://github.com/FreshRSS/FreshRSS/archive/master.tar.gz' /opt
			mv /opt/FreshRSS-master /opt/FreshRSS

		fi

		software_id=28 # TigerVNC Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			# TigerVNC allows connecting to a virtual desktop
			# X11VNC allows connecting to a real desktop session, thus shared desktop sessions as well
			# netbase is required until Bullseye to solve: "Use of uninitialized value $proto in socket at /usr/bin/tigervncserver"
			# - It is a recommendation and no dependency of perl, but expected by some packages depending on perl only: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=939055
			local netbase=
			(( $G_DISTRO < 6 )) && netbase='netbase'
			# https://github.com/MichaIng/DietPi/issues/1558#issuecomment-701547904
			local dbus_package=
			(( ${aSOFTWARE_INSTALL_STATE[23]} > 0 )) && dbus_package='dbus-user-session'
			G_AGI tigervnc-standalone-server tigervnc-common x11vnc $netbase $dbus_package

		fi

		software_id=120 # RealVNC Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI realvnc-vnc-server

		fi

		software_id=73 # Fail2Ban
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Create jail.conf (backend = systemd) first, to prevent APT failure due to missing /var/log/auth.log: https://github.com/MichaIng/DietPi/issues/475#issuecomment-310873879
			G_EXEC mkdir -p /etc/fail2ban/fail2ban.d
			[[ -f '/etc/fail2ban/jail.conf' ]] || cat << '_EOF_' > /etc/fail2ban/jail.conf
[DEFAULT]
enabled = true
ignoreip = 127.0.0.1/8
ignorecommand =
backend = systemd
mode = normal
filter = %(__name__)s[mode=%(mode)s]
findtime = 600
maxretry = 3
bantime = 600
banaction = route
action = %(banaction)s[blocktype=blackhole]

[dropbear]

[sshd]
# Mode: normal (default), ddos, extra or aggressive (combines all)
# See "filter.d/sshd.conf" for details.
#mode = normal
_EOF_
			# Log to systemd by default
			local logtarget='SYSOUT'
			(( $G_DISTRO < 5 )) && logtarget='SYSLOG'
			echo -e "[Definition]\nlogtarget = $logtarget" > /etc/fail2ban/fail2ban.d/97_dietpi.conf
			unset -v logtarget

			G_AGI python3-systemd fail2ban

			# Remove obsolete sysvinit service and traces
			rm -f /etc/{init.d,default}/fail2ban
			update-rc.d -f fail2ban remove

			# Failsafe
			G_EXEC systemctl unmask fail2ban
			G_EXEC systemctl enable --now fail2ban

		fi

		software_id=74 # InfluxDB
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			INSTALL_URL_ADDRESS='https://repos.influxdata.com/influxdb.key'
			G_CHECK_URL "$INSTALL_URL_ADDRESS"

			G_EXEC eval "curl -sSLf '$INSTALL_URL_ADDRESS' | gpg --dearmor -o /etc/apt/trusted.gpg.d/dietpi-influxdb.gpg --yes"
			# Use Buster branch on Bullseye
			echo "deb https://repos.influxdata.com/debian/ ${G_DISTRO_NAME/bullseye/buster} stable" > /etc/apt/sources.list.d/influxdb.list
			G_AGUP

			G_AGI influxdb

		fi

		software_id=77 # Grafana
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# ARMv6: Install package manually since repo is not compatible: https://grafana.com/grafana/download?platform=arm
			if (( $G_HW_ARCH == 1 )); then

				Download_Install 'https://dl.grafana.com/oss/release/grafana-rpi_7.4.3_armhf.deb'

			# Else use official APT repo: https://grafana.com/docs/grafana/latest/installation/debian/#install-from-apt-repository
			else

				# APT key
				INSTALL_URL_ADDRESS='https://packages.grafana.com/gpg.key'
				G_CHECK_URL "$INSTALL_URL_ADDRESS"
				G_EXEC eval "curl -sSLf '$INSTALL_URL_ADDRESS' | gpg --dearmor -o /etc/apt/trusted.gpg.d/dietpi-grafana.gpg --yes"

				# APT list
				echo 'deb https://packages.grafana.com/oss/deb/ stable main' > /etc/apt/sources.list.d/grafana.list
				G_AGUP

				# APT package
				G_AGI grafana

			fi

		fi

		software_id=64 # phpSysInfo
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Download
			Download_Install 'https://github.com/phpsysinfo/phpsysinfo/archive/master.tar.gz'

			# Clean reinstall, but preserve previous config
			if [[ -d '/var/www/phpsysinfo' ]]
			then
				G_EXEC mv /var/www/phpsysinfo/phpsysinfo.ini phpsysinfo-master/phpsysinfo.ini
				G_EXEC rm -R /var/www/phpsysinfo
			fi
			G_EXEC mv phpsysinfo-master /var/www/phpsysinfo

		fi

		software_id=80 # Ubooquity
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			G_EXEC curl -sSfL https://vaemendis.net/ubooquity/service/download.php -o Ubooquity.zip
			G_EXEC unzip -o Ubooquity.zip
			G_EXEC_NOEXIT=1 G_EXEC rm Ubooquity.zip
			G_EXEC mkdir -p /mnt/dietpi_userdata/ubooquity
			G_EXEC mv {,/mnt/dietpi_userdata/ubooquity/}Ubooquity.jar

		fi

		software_id=179 # Komga
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			local fallback_url='https://github.com/gotson/komga/releases/download/v0.69.2/komga-0.69.2.jar'
			Download_Install "$(curl -sSfL 'https://api.github.com/repos/gotson/komga/releases/latest' | mawk -F\" '/"browser_download_url": .*\/komga-[^"\/]*\.jar"/{print $4}')" /mnt/dietpi_userdata/komga/komga.jar

		fi

		software_id=56 # Single File PHP Gallery
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			# Install required PHP modules: https://sye.dk/sfpg/
			DEPS_LIST="$PHP_NAME-gd"
			Download_Install 'https://dietpi.com/downloads/binaries/all/Single_File_PHP_Gallery.7z' /var/www/gallery

		fi

		software_id=40 # Ampache
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			Download_Install 'https://github.com/ampache/ampache/archive/master.tar.gz'
			# Preserve existing config files
			[[ -f '/var/www/ampache/config/ampache.cfg.php' ]] && G_EXEC mv /var/www/ampache/config/ampache.cfg.php ampache-master/config/
			[[ -f '/var/www/ampache/config/registration_agreement.php' ]] && G_EXEC mv /var/www/ampache/config/registration_agreement.php ampache-master/config/
			[[ -f '/var/www/ampache/channel/.htaccess' ]] && G_EXEC mv /var/www/ampache/channel/.htaccess ampache-master/channel/
			[[ -f '/var/www/ampache/rest/.htaccess' ]] && G_EXEC mv /var/www/ampache/rest/.htaccess ampache-master/rest/
			[[ -f '/var/www/ampache/play/.htaccess' ]] && G_EXEC mv /var/www/ampache/play/.htaccess ampache-master/play/
			[[ -d '/var/www/ampache' ]] && G_EXEC rm -R /var/www/ampache
			G_EXEC mv ampache-master /var/www/ampache

			# Download and install composer
			G_EXEC curl -sSfL https://getcomposer.org/composer-1.phar -o /usr/local/bin/composer
			G_EXEC chmod +x /usr/local/bin/composer

			# Install Ampache
			G_EXEC cd /var/www/ampache
			G_EXEC_OUTPUT=1 G_EXEC composer install --prefer-source --no-interaction
			G_EXEC cd /tmp/$G_PROGRAM_NAME

		fi

		software_id=97 # OpenVPN Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI openvpn iptables
			# Remove obsolete sysvinit service
			[[ -f '/etc/init.d/openvpn' ]] && rm /etc/init.d/openvpn
			update-rc.d -f openvpn remove

		fi

		software_id=117 # PiVPN
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			INSTALL_URL_ADDRESS='https://install.pivpn.io'
			G_CHECK_URL "$INSTALL_URL_ADDRESS"

			G_EXEC curl -sSfL "$INSTALL_URL_ADDRESS" -o install.bash
			G_EXEC chmod +x install.bash
			G_DIETPI-NOTIFY 2 'Preventing reboot from within PiVPN installer'
			G_EXEC sed -i '/shutdown[[:space:]]/d' install.bash
			G_DIETPI-NOTIFY 2 'Preventing install of unattended-upgrades'
			G_EXEC sed -i 's/^[[:blank:]]*UNATTUPG=[[:digit:]]/UNATTUPG=0/' install.bash

			# APT deps
			G_AGI dnsutils net-tools bsdmainutils iptables-persistent # https://github.com/pivpn/pivpn/blob/master/auto_install/install.sh#L40

			G_EXEC_OUTPUT=1 G_EXEC_NOEXIT=1 G_EXEC ./install.bash || aSOFTWARE_INSTALL_STATE[$software_id]=0
			G_EXEC_NOHALT=1 G_EXEC rm install.bash

		fi

		software_id=172 # WireGuard
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Packages available on Buster backports and Bullseye: For Stretch and Raspbian Buster add Bullseye repo
			if (( $G_DISTRO < 5 || ( $G_HW_MODEL < 10 && ${G_RASPBIAN:-1} && $G_DISTRO == 5 ) )); then

				# Raspbian or Debian?
				local url='http://raspbian.raspberrypi.org/raspbian/'
				(( $G_HW_MODEL > 9 )) || (( ! $G_RASPBIAN )) && url='https://deb.debian.org/debian/'

				echo "deb $url bullseye main" > /etc/apt/sources.list.d/dietpi-wireguard.list

				# Disable repo via priority "-1", to prevent any accidental package upgrades: https://github.com/MichaIng/DietPi/issues/2568
				# Enable but set WireGuard package priorities low enough to install only if not available in main repo(s)
				echo -e 'Package: *\nPin: release n=bullseye\nPin-Priority: -1\n
Package: wireguard wireguard-dkms wireguard-tools\nPin: release n=bullseye\nPin-Priority: 100' > /etc/apt/preferences.d/dietpi-wireguard

				G_AGUP

			fi

			# If the kernel module is shipped with the kernel (e.g. Armbian 5.X)
			if [[ $WIREGUARD_BUILTIN == 1 ]]; then

				# Purge DKMS as it is not required and might mess with available kernel module
				dpkg-query -s wireguard-dkms &> /dev/null && G_AGP wireguard-dkms
				# Install userspace tools only
				G_AGI wireguard-tools iptables qrencode

			# Else, kernel headers and matching kernel image is required for wireguard-dkms to build the WireGuard kernel module
			else

				local apackages=()
				# x86_64
				if (( $G_HW_ARCH == 10 )); then

					apackages=('linux-image-amd64' 'linux-headers-amd64')

				# RPi
				elif (( $G_HW_MODEL < 10 )); then

					# Install bootloader package as well to assure we have a correctly booting system with intended kernel
					apackages=('raspberrypi-kernel' 'raspberrypi-kernel-headers' 'raspberrypi-bootloader')

				# Odroid XU4
				elif (( $G_HW_MODEL == 11 )); then

					apackages=('linux-image-4.14-armhf-odroid-xu4' 'linux-headers-4.14-armhf-odroid-xu4')

				# Odroid C2
				elif (( $G_HW_MODEL == 12 )); then

					apackages=('linux-image-arm64-odroid-c2' 'linux-headers-arm64-odroid-c2')

				# Odroid N1
				elif (( $G_HW_MODEL == 14 )); then

					apackages=('linux-image-arm64-odroid-n1' 'linux-headers-arm64-odroid-n1')

				# Odroid N2
				elif (( $G_HW_MODEL == 15 )); then

					apackages=('linux-image-arm64-odroid-n2' 'linux-headers-arm64-odroid-n2')

				# Odroid C4
				elif (( $G_HW_MODEL == 16 )); then

					apackages=('linux-image-arm64-odroid-c4' 'linux-headers-arm64-odroid-c4')

				fi

				# Odroids need to purge before we install, else, does not update to latest version...
				(( $G_HW_MODEL > 9 && $G_HW_MODEL < 20 )) && G_AGP "${apackages[@]}"

				# Since G_AGUG does not upgrade packages with changed dependencies, in case of kernel image meta packages, those need to be installed+upgraded via G_AGI.
				G_AGI "${apackages[@]}" # apt-get install overrides hold state
				unset -v apackages

				# Workaround for missing /lib/modules/<version>/build symlink: https://github.com/MichaIng/DietPi/issues/3577
				# - Known as issue currently on Odroid C2/N1/N2, check for new build system if linux-<version>/ holds current headers instead of linux-source-<version>/: https://dietpi.com/meveric/pool/c2/l/
				for i in /lib/modules/*
				do
					[[ -d $i/build ]] && continue
					[[ -d /usr/src/linux-headers-${i##*/} ]] && ln -sfv "/usr/src/linux-headers-${i##*/}" "$i/build"
				done

				# If existing install, reconfigure to rebuild WireGuard kernel module against current kernel + headers
				dpkg-query -s wireguard-dkms &> /dev/null && G_EXEC dpkg-reconfigure -f noninteractive wireguard-dkms

				# iptables required to forward incoming VPN traffic to local LAN/internet interface
				# qrencode required to print client config QR code to console
				G_AGI wireguard-dkms wireguard-tools iptables qrencode

			fi

		fi

		software_id=171 # DietPi-NordVPN
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI openvpn

			Download_Install 'https://downloads.nordcdn.com/configs/archives/servers/ovpn.zip' /etc/openvpn

			G_EXEC mkdir -p /var/lib/dietpi/dietpi-software/installed/dietpi-nordvpn

			G_WHIP_MSG '[ INFO ] DietPi-NordVPN has been installed.\n\nOnce DietPi-Software has finished all other installations, you can run the GUI using the following command:\n - dietpi-nordvpn'

		fi

		software_id=92 # Certbot
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			if (( ${aSOFTWARE_INSTALL_STATE[83]} > 0 )); then

				G_AGI certbot python3-certbot-apache

			elif (( ${aSOFTWARE_INSTALL_STATE[85]} > 0 )); then

				G_AGI certbot python3-certbot-nginx

			else

				G_AGI certbot

			fi

		fi

		software_id=60 # WiFi Hotspot
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			DEPS_LIST='hostapd isc-dhcp-server iptables libnl-3-200'

			# RTL8188C* device and special Realtek hostapd package available
			if (( $WIFIHOTSPOT_RTL8188C_DEVICE && $WIFIHOTSPOT_RTL8188C_PACKAGE )); then

				# shellcheck disable=SC2086
				G_AGI ${DEPS_LIST/hostapd/hostapd-realtek}

			# RTL8188C* without special Realtek hostapd package: On non-RPi ARM, install our binaries with "rtl1871xdrv" driver. On RPi, a RTL8188C*-compatible hostapd package is shipped.
			elif (( $WIFIHOTSPOT_RTL8188C_DEVICE && $G_HW_MODEL > 9 && $G_HW_ARCH < 4 )); then

				Download_Install 'https://dietpi.com/downloads/binaries/all/hostapd_2.5_all.zip'

				# Which binary to install
				local filename_hostapd{,_cli}

				# ARMv6
				if (( $G_HW_ARCH == 1 )); then

					filename_hostapd='hostapd-rtl8188c-armv6'
					filename_hostapd_cli='hostapd_cli-armv6'

				# ARMv7
				elif (( $G_HW_ARCH == 2 )); then

					filename_hostapd='hostapd-rtl8188c-armv7'
					filename_hostapd_cli='hostapd_cli-armv7'

				# ARMv8
				elif (( $G_HW_ARCH == 3 )); then

					filename_hostapd='hostapd-rtl8188c-arm64'
					filename_hostapd_cli='hostapd_cli-arm64'

				fi

				G_EXEC mv "$filename_hostapd" /usr/sbin/hostapd
				G_EXEC mv "$filename_hostapd_cli" /usr/sbin/hostapd_cli
				G_EXEC chmod +x /usr/sbin/hostapd{,_cli}

				G_EXEC rm hostapd*

			# Else install the default package.
			# NB: Debian Stretch ships an older version then our own compiled binaries, but many device/firmware repos (RPi, Meveric, Armbian) ship newer ones even for Stretch, thus we should not overwrite them anymore.
			else

				# shellcheck disable=SC2086
				G_AGI $DEPS_LIST

			fi
			DEPS_LIST=

			# Enable WiFi modules
			/boot/dietpi/func/dietpi-set_hardware wifimodules enable

			# Unmask hostapd service: https://github.com/MichaIng/DietPi/issues/2962
			G_EXEC systemctl unmask hostapd

		fi

		software_id=61 # Tor Hotspot
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI tor

		fi

		software_id=37 # Shairport-Sync
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			Download_Install "https://dietpi.com/downloads/binaries/$G_DISTRO_NAME/shairport-sync_$G_HW_ARCH_NAME.deb"

		fi

		software_id=48 # Pydio
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Install required PHP modules
			DEPS_LIST="$PHP_NAME-apcu $PHP_NAME-gd $PHP_NAME-intl $PHP_NAME-mbstring $PHP_NAME-opcache $PHP_NAME-xml"

			# Skip install, if already present
			if [[ -d /var/www/pydio ]]; then

				G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} install dir \"/var/www/pydio\" already exists. Download and install steps will be skipped.
 - Please manually backup your config files+data, remove the install dir and rerun \"dietpi-software (re)install $software_id\" if you need to reinstall.
 - If you want to update the ${aSOFTWARE_NAME[$software_id]} instance, please use the internal updater from WebUI."
				# shellcheck disable=SC2086
				G_AGI $DEPS_LIST
				DEPS_LIST=

			else

				Download_Install 'https://download.pydio.com/pub/core/ci/pydio-latest.tar.gz' /var/www
				mv /var/www/pydio-latest /var/www/pydio

			fi

		fi

		software_id=36 # Squeezelite
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			Download_Install "https://dietpi.com/downloads/binaries/$G_DISTRO_NAME/squeezelite_$G_HW_ARCH_NAME.deb"

		fi

		software_id=99 # EmonPi
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			DEPS_LIST='minicom python-serial python-configobj python-pip python-dev'
			Download_Install 'https://github.com/Fourdee/emonhub/archive/emon-pi.zip'

			pip2 install -U pip setuptools wheel
			pip2 install -U paho-mqtt pydispatcher

			# Move everything to /etc/emonhub
			[[ -d '/etc/emonhub' ]] && rm -R /etc/emonhub
			mv emonhub-* /etc/emonhub

		fi

		software_id=66 # RPi Monitor
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			Download_Install 'https://github.com/XavierBerger/RPi-Monitor-deb/raw/master/packages/rpimonitor_2.12-r0_all.deb'

		fi

		software_id=65 # Netdata
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# No package available on Stretch
			if (( $G_DISTRO < 5 )); then

				# For ARMv6 RPi use our self-compiled package
				if (( $G_HW_ARCH == 1 )); then

					DEPS_LIST='zlib1g-dev'
					Download_Install 'https://dietpi.com/downloads/binaries/all/netdata_1.11.1-1_armv6l.deb'

				# Else use the packages from Debian Stretch backports snapshot, that were removed recently, hosted now on dietpi.com
				else

					Download_Install "https://dietpi.com/downloads/binaries/stretch/netdata_$G_HW_ARCH_NAME.7z"
					G_AGI ./netdata{-core,-plugins-bash,-web,}_*.deb
					G_AGF
					rm netdata*

				fi

			else

				G_AGI netdata

			fi

		fi

		software_id=57 # Baïkal
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# APT deps: https://github.com/sabre-io/Baikal/wiki/Baïkal-dependencies
			DEPS_LIST="$PHP_NAME-xml $PHP_NAME-mbstring $PHP_NAME-mysql"

			local fallback_url='https://github.com/sabre-io/Baikal/releases/download/0.8.0/baikal-0.8.0.zip'
			Download_Install "$(curl -sSfL 'https://api.github.com/repos/sabre-io/Baikal/releases/latest' | mawk -F\" '/"browser_download_url": .*\/baikal-[^"\/]*\.zip"/{print $4}')"

			# Reinstall: https://sabre.io/baikal/upgrade/
			if [[ -d '/var/www/baikal' ]]; then

				[[ -d '/var/www/baikal/Specific' ]] && G_EXEC cp -a /var/www/baikal/Specific/. baikal/Specific/
				[[ -d '/var/www/baikal/config' ]] && G_EXEC cp -a /var/www/baikal/config/. baikal/config/
				G_EXEC rm -R /var/www/baikal

			fi
			G_EXEC mv baikal /var/www/baikal

		fi

		software_id=43 # Mumble Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI mumble-server

		fi

		software_id=41 # Emby Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# ARMv7
			local arch='armhf'

			# ARMv8
			if (( $G_HW_ARCH == 3 )); then

				arch='arm64'

			# x86_64
			elif (( $G_HW_ARCH == 10 )); then

				arch='amd64'

			fi

			local fallback_url="https://github.com/MediaBrowser/Emby.Releases/releases/download/4.5.4.0/emby-server-deb_4.5.4.0_$arch.deb"
			Download_Install "$(curl -sSfL 'https://api.github.com/repos/MediaBrowser/Emby.Releases/releases/latest' | grep "\"browser_download_url\": .*/emby-server-deb_[^\"/]*_$arch\.deb\"" | cut -d \" -f 4)"

		fi

		software_id=42 # Plex Media Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Apply APT key
			INSTALL_URL_ADDRESS='https://downloads.plex.tv/plex-keys/PlexSign.key'
			G_CHECK_URL "$INSTALL_URL_ADDRESS"
			curl -sSfL "$INSTALL_URL_ADDRESS" | apt-key add -

			# Apply APT repo
			echo 'deb https://downloads.plex.tv/repo/deb/ public main' > /etc/apt/sources.list.d/plexmediaserver.list

			# Update APT lists
			G_AGUP

			# Install PMS
			G_AGI plexmediaserver

		fi

		software_id=52 # Cuberite
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# x86_64
			if (( $G_HW_ARCH == 10 )); then

				INSTALL_URL_ADDRESS='https://download.cuberite.org/linux-x86_64/Cuberite.tar.gz'

			# ARMv6/7
			elif [[ $G_HW_ARCH == [12] ]]; then

				INSTALL_URL_ADDRESS='https://download.cuberite.org/linux-armhf-raspbian/Cuberite.tar.gz'

			fi

			Download_Install "$INSTALL_URL_ADDRESS" /mnt/dietpi_userdata/cuberite

			# ARMv6 workaround: https://github.com/MichaIng/DietPi/issues/3664
			(( $G_HW_MODEL == 1 )) && Download_Install 'https://dietpi.com/downloads/binaries/buster/cuberite_armv6l.7z' /mnt/dietpi_userdata/cuberite

		fi

		software_id=53 # MineOS
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 ))
		then
			Banner_Installing # https://minecraft.codeemo.com/mineoswiki/index.php?title=MineOS-node_(apt-get)

			INSTALL_URL_ADDRESS='https://github.com/hexparrot/mineos-node.git'
			G_CHECK_URL "$INSTALL_URL_ADDRESS"

			# APT deps
			G_AGI python rdiff-backup rsync screen

			# Download/Update MineOS
			G_EXEC mkdir -p /mnt/dietpi_userdata/mineos
			G_EXEC cd /mnt/dietpi_userdata/mineos
			if [[ -d 'minecraft' ]]
			then
				# https://minecraft.codeemo.com/mineoswiki/index.php?title=Updating_the_Webui
				G_EXEC cd minecraft
				if [[ -x 'update_webui.sh' ]]
				then
					G_EXEC_OUTPUT=1 G_EXEC ./update_webui.sh
				else
					G_EXEC_OUTPUT=1 G_EXEC git fetch
					G_EXEC_OUTPUT=1 G_EXEC git merge origin/master
				fi
			else
				G_EXEC_OUTPUT=1 G_EXEC git clone "$INSTALL_URL_ADDRESS" minecraft
				G_EXEC cd minecraft
			fi

			# File modes
			G_EXEC git config core.filemode false
			G_EXEC chmod +x mineos_console.js webui.js update_webui.sh reset_webui.sh generate-sslcert.sh

			# Install Node 11, as MineOS is currently not compatible with newer Node versions: https://github.com/hexparrot/mineos-node/issues/374
			G_EXEC_OUTPUT=1 G_EXEC npm i -g --unsafe-perm n
			G_EXEC_OUTPUT=1 G_EXEC n 11

			# Install MineOS
			G_EXEC_OUTPUT=1 G_EXEC npm i --unsafe-perm
			G_EXEC cd /tmp/$G_PROGRAM_NAME
		fi

		software_id=49 # Gogs
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# ARMv6: No pre-compiled binaries available, thus we use our own: https://github.com/gogs/gogs/releases
			if (( $G_HW_ARCH == 1 )); then

				INSTALL_URL_ADDRESS='https://dietpi.com/downloads/binaries/all/gogs_armv6.zip'

			# Else install latest binaries from GitHub
			else

				# ARMv7
				local arch='armv7'

				# ARMv8
				if (( $G_HW_ARCH == 3 )); then

					arch='armv8'

				# x86_64
				elif (( $G_HW_ARCH == 10 )); then

					arch='amd64'

				fi

				local fallback_url="https://github.com/gogs/gogs/releases/download/v0.12.3/gogs_0.12.3_linux_$arch.tar.gz"
				INSTALL_URL_ADDRESS=$(curl -sSfL 'https://api.github.com/repos/gogs/gogs/releases/latest' | grep "\"browser_download_url\": .*/gogs_[^\"/]*_linux_$arch.tar.gz\"" | cut -d \" -f 4)

			fi

			Download_Install "$INSTALL_URL_ADDRESS"

			# Remove old install dir, but preserve existing configs
			if [[ -d '/etc/gogs' ]]; then

				[[ -d '/etc/gogs/custom' ]] && G_EXEC mv /etc/gogs/custom gogs
				G_EXEC mv /etc/gogs/.??* gogs # dot files = SSH and Git user configs
				G_EXEC rm -R /etc/gogs

			fi

			G_EXEC mv gogs /etc/gogs

		fi

		software_id=46 # qBittorrent
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI qbittorrent-nox

		fi

		software_id=107 # rTorrent
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			DEPS_LIST='rtorrent mediainfo'
			# On Buster, no "screen" is required to run rTorrent as daemon: https://github.com/rakshasa/rtorrent/wiki/Daemon_Mode
			(( $G_DISTRO < 5 )) && DEPS_LIST+=' screen'

			# Install ruTorrent: Web UI for rTorrent
			# - Grab current version
			local version=$(curl -sSfL 'https://api.github.com/repos/Novik/ruTorrent/releases/latest' | mawk -F\" '/"tag_name": /{print $4}')
			[[ $version ]] || { version='v3.10'; G_DIETPI-NOTIFY 1 "Automatic latest ruTorrent version detection failed. Version \"$version\" will be installed as fallback, but a newer version might be available. Please report this at: https://github.com/MichaIng/DietPi/issues"; }
			Download_Install "https://github.com/Novik/ruTorrent/archive/$version.tar.gz"

			# - Reinstall
			if [[ -d '/var/www/rutorrent' ]]; then

				# Backup known config files
				G_BACKUP_FP /var/www/rutorrent/conf/config.php
				G_BACKUP_FP /var/www/rutorrent/conf/access.ini
				G_BACKUP_FP /var/www/rutorrent/conf/plugins.ini

				# Merge new install into old to preserve e.g. 3rd party plugins
				G_EXEC cp -a ruTorrent-*/. /var/www/rutorrent/
				G_EXEC rm -R ruTorrent-*

			# - Fresh install
			else

				G_EXEC mv ruTorrent-* /var/www/rutorrent

			fi

			# Install DarkBetter theme manually: https://github.com/MichaIng/DietPi/issues/3271
			if [[ -d '/var/www/rutorrent/plugins/theme/themes/DarkBetter' ]]; then

				Download_Install 'https://github.com/chocolatkey/DarkBetter/archive/master.tar.gz'
				rm -R /var/www/rutorrent/plugins/theme/themes/DarkBetter
				mv DarkBetter-master /var/www/rutorrent/plugins/theme/themes/DarkBetter

			fi

		fi

		software_id=132 # Aria2
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			G_AGI aria2

			# Web UI: Settings are stored client-wise, web UI files are never written by webserver. Thus root:root 022 permissions existing dir removal on reinstall can be done.
			Download_Install 'https://github.com/ziahamza/webui-aria2/archive/master.tar.gz'
			[[ -d '/var/www/aria2' ]] && G_EXEC rm -R /var/www/aria2
			G_EXEC mv webui-aria2-master /var/www/aria2

		fi

		software_id=116 # Medusa
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			DEPS_LIST='mediainfo python3'

			if [[ -d '/mnt/dietpi_userdata/medusa' ]]; then

				# shellcheck disable=SC2086
				G_AGI $DEPS_LIST
				DEPS_LIST=
				G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} install dir \"/mnt/dietpi_userdata/medusa\" already exists. Download and install steps will be skipped.
 - If you want to update ${aSOFTWARE_NAME[$software_id]}, please use the internal updater from WebUI.
 - If you need to reinstall (e.g. broken instance), please manually backup your config files+data, remove the install dir and rerun \"dietpi-software (re)install $software_id\"."

			else

				Download_Install 'https://github.com/pymedusa/Medusa/archive/master.tar.gz' /mnt/dietpi_userdata
				G_EXEC mv /mnt/dietpi_userdata/{Medusa-master,medusa}

			fi

		fi

		software_id=50 # Syncthing
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			if [[ -d '/opt/syncthing' ]]; then

				G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} install dir \"/opt/syncthing\" already exists. Download and install steps will be skipped.
 - If you want to update ${aSOFTWARE_NAME[$software_id]}, please use the internal updater from web UI.
 - if you need to reinstall (e.g. broken instance), please manually backup your config files+data, remove the install dir \"/opt/syncthing\" and rerun \"dietpi-software (re)install $software_id\"."

			else

				# ARMv6/7
				local arch='arm'

				# ARMv8
				if (( $G_HW_ARCH == 3 )); then

					arch+='64'

				# x86_64
				elif (( $G_HW_ARCH == 10 )); then

					arch='amd64'

				fi

				local fallback_url="https://github.com/syncthing/syncthing/releases/download/v1.13.1/syncthing-linux-$arch-v1.13.1.tar.gz"
				Download_Install "$(curl -sSfL 'https://api.github.com/repos/syncthing/syncthing/releases/latest' | grep "\"browser_download_url\": .*/syncthing-linux-$arch-[^\"/]*\.tar\.gz\"" | cut -d \" -f 4)"
				G_EXEC mv syncthing-* /opt/syncthing

			fi

		fi

		software_id=113 # Chromium
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			if (( $G_HW_MODEL < 10 )); then

				G_AGI chromium-browser

			else

				G_AGI chromium

			fi

		fi

		software_id=136 # MotionEye
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Prereqs + Motion
			G_AGI v4l-utils python-pip python-dev curl libssl-dev libcurl4-openssl-dev libjpeg-dev zlib1g-dev motion

			# Motioneye
			pip2 install -U pip setuptools wheel
			pip2 install -U motioneye

		fi

		software_id=137 # mjpg-streamer
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 ))
		then
			Banner_Installing

			# Build dependencies
			DEPS_LIST='make cmake gcc libc6-dev libjpeg-dev'
			# - On RPi, add RPi Camera support by default
			(( $G_HW_MODEL > 9 )) || DEPS_LIST+=' libraspberrypi-dev'

			# Download sources
			Download_Install 'https://github.com/jacksonliam/mjpg-streamer/archive/master.tar.gz'

			# Compile
			G_EXEC cd mjpg-streamer-master/mjpg-streamer-experimental
			G_EXEC_OUTPUT=1 G_EXEC make CFLAGS='-g0 -O3' -j "$(nproc)"
			G_EXEC strip --remove-section=.comment --remove-section=.note _build/mjpg_streamer

			# Move all plugin libraries into the executable's directory to avoid the need for LD_LIBRARY_PATH
			G_EXEC mv _build/plugins/*/*.so _build/

			# Remove all left build files
			G_EXEC rm -R _build/{{,C,c}[Mm]ake*,plugins}

			# Install to system, in case remove previous installs
			[[ -d '/opt/mjpg-streamer' ]] && G_EXEC rm -R /opt/mjpg-streamer
			G_EXEC mv _build /opt/mjpg-streamer

			# Cleanup
			G_EXEC cd /tmp/$G_PROGRAM_NAME
			G_EXEC rm -R mjpg-streamer-master
		fi

		software_id=138 # VirtualHere
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			INSTALL_URL_ADDRESS='https://virtualhere.com/sites/default/files/usbserver/vhusbd'

			# ARMv6/7
			if [[ $G_HW_ARCH == [12] ]]; then

				INSTALL_URL_ADDRESS+='arm'

			# ARMv8
			elif (( $G_HW_ARCH == 3 )); then

				INSTALL_URL_ADDRESS+='arm64'

			# x86_64
			elif (( $G_HW_ARCH == 10 )); then

				INSTALL_URL_ADDRESS+='x86_64'

			fi

			G_CHECK_URL "$INSTALL_URL_ADDRESS"

			G_EXEC mkdir -p /etc/vhusbd
			G_EXEC curl -sSfL "$INSTALL_URL_ADDRESS" -o /etc/vhusbd/vhusbd
			G_EXEC chmod +x /etc/vhusbd/vhusbd

		fi

		software_id=139 # SABnzbd
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing # https://sabnzbd.org/wiki/installation/install-off-modules

			# APT deps
			DEPS_LIST='par2 p7zip-full'
			# - Pre-compiling required on ARM
			(( $G_HW_ARCH > 9 )) || DEPS_LIST+=' libffi-dev libssl-dev'

			# Stretch: SABnzbd v3.2.0 raised the Python dependency to v3.6: https://github.com/sabnzbd/sabnzbd/tree/3.2.0
			local version='master'
			(( $(python3 -V | tr -cd '0-9') < 360 )) && version='3.1.1'

			# Download
			Download_Install "https://github.com/sabnzbd/sabnzbd/archive/$version.tar.gz"

			# Reinstall: Remove old install dir
			if [[ -d '/etc/sabnzbd' ]]; then

				# Preserve old config file
				[[ -f '/etc/sabnzbd/sabnzbd.ini' ]] && G_EXEC mv /etc/sabnzbd/sabnzbd.ini sabnzbd-$version/sabnzbd.ini
				G_EXEC rm -R /etc/sabnzbd

			fi

			# Install
			G_EXEC mv sabnzbd-$version /etc/sabnzbd

			# Python deps
			G_EXEC cd /etc/sabnzbd
			G_EXEC_OUTPUT=1 G_EXEC pip3 install -Ur requirements.txt
			G_EXEC cd /tmp/$G_PROGRAM_NAME

		fi

		software_id=177 # Firefox Sync Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Dependencies: https://github.com/mozilla-services/syncserver#quickstart
			DEPS_LIST='python-virtualenv python-dev libmariadb-dev'
			# - Additional module compiling required on ARM
			(( $G_HW_ARCH < 10 )) && DEPS_LIST+=' libffi-dev libssl-dev'

			# Download & Install
			local commit='bfbc3abd36ee4db70df13a9c43f7758a1528c965' # https://github.com/MichaIng/DietPi/issues/3774#issuecomment-703230290
			INSTALL_URL_ADDRESS="https://github.com/mozilla-services/syncserver/archive/$commit.tar.gz"
			Download_Install "$INSTALL_URL_ADDRESS"
			[[ -d '/opt/firefox-sync' ]] && G_EXEC rm -R /opt/firefox-sync
			G_EXEC mv syncserver-$commit /opt/firefox-sync

			# Build
			G_EXEC cd /opt/firefox-sync
			G_EXEC_OUTPUT=1 G_EXEC make build
			G_EXEC cd /tmp/$G_PROGRAM_NAME

		fi

		software_id=183 # Bitwarden_RS
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Dependencies: https://github.com/dani-garcia/bitwarden_rs/wiki/Building-binary#dependencies
			DEPS_LIST='pkg-config libssl-dev'

			# Download
			local version=$(curl -sSfL 'https://api.github.com/repos/dani-garcia/bitwarden_rs/releases/latest' | mawk -F\" '/"tag_name": /{print $4}')
			[[ $version ]] || { version='1.19.0'; G_DIETPI-NOTIFY 1 "Automatic latest ${aSOFTWARE_NAME[$software_id]} version detection failed. Version \"$version\" will be installed as fallback, but a newer version might be available. Please report this at: https://github.com/MichaIng/DietPi/issues"; }
			Download_Install "https://github.com/dani-garcia/bitwarden_rs/archive/$version.tar.gz"

			# Replace old instance on reinstall
			[[ -d '/opt/bitwarden_rs' ]] && G_EXEC rm -R /opt/bitwarden_rs
			G_EXEC mv "bitwarden_rs-$version" /opt/bitwarden_rs

			# Assure 2 GiB overall memory (-100 MiB to avoid tiny swap space) is available and limit concurrent cargo build jobs to 2 if less than 3 GiB memory is available.
			local jobs=
			if (( $RAM_TOTAL < 1948 ))
			then
				G_DIETPI-NOTIFY 2 'Bitwaden_RS build requires at least 2 GiB memory. We will now increase your swap size to satisfy this requirement.'
				/boot/dietpi/func/dietpi-set_swapfile 1
				(( $G_HW_CPU_CORES > 2 )) && jobs=2

			elif (( $RAM_TOTAL < 3072 && $G_HW_CPU_CORES > 2 ))
			then
				jobs=2
			fi

			# Temporarily assure 1.5 GiB /tmp size for temporary Rust install
			(( $(findmnt -bno SIZE /tmp) < 1610612736 )) && G_EXEC mount -o remount,size=1610612736 /tmp

			# Clean APT cache (which includes temporarily downloaded archives) when moved to RAM
			[[ -d '/tmp/apt' ]] && G_EXEC apt-get clean

			# Override $HOME to allow temporary Rust install to RAMdisk and DietPi-Software working directory, so all leftovers are automatically removed on script exit and/or reboot
			export HOME="/tmp/$G_PROGRAM_NAME"

			# Install Rust via https://rustup.rs/
			G_EXEC curl -sSfL https://sh.rustup.rs -o rustup-init.sh
			G_EXEC chmod +x rustup-init.sh
			G_EXEC ./rustup-init.sh -y --default-toolchain none
			G_EXEC_NOHALT=1 G_EXEC rm rustup-init.sh
			G_EXEC . .cargo/env

			# Build
			G_EXEC cd /opt/bitwarden_rs
			G_EXEC_OUTPUT=1 G_EXEC cargo build ${jobs:+-j $jobs} --features sqlite --release
			G_EXEC cd /tmp/$G_PROGRAM_NAME

			# Uninstall rust after compiling
			G_EXEC rustup self uninstall -y

			# Restore $HOME
			HOME='/root'

			# Install web vault
			local fallback_url='https://github.com/dani-garcia/bw_web_builds/releases/download/v2.18.1b/bw_web_v2.18.1b.tar.gz'
			Download_Install "$(curl -sSfL 'https://api.github.com/repos/dani-garcia/bw_web_builds/releases/latest' | mawk -F\" '/"browser_download_url": .*\/bw_web_[^"\/]*\.tar\.gz"/{print $4}')" /mnt/dietpi_userdata/bitwarden_rs

		fi

		software_id=141 # Spotify Connect Web
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			Download_Install 'https://github.com/Fornoth/spotify-connect-web/releases/download/0.0.4-alpha/spotify-connect-web_0.0.4-alpha.tar.gz' /mnt/dietpi_userdata

		fi

		software_id=142 # CouchPotato
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			DEPS_LIST='libffi-dev libssl-dev python-lxml python-pip python-dev'
			Download_Install 'https://github.com/CouchPotato/CouchPotatoServer/archive/master.tar.gz'

			[[ -d '/etc/couchpotato' ]] && rm -R /etc/couchpotato
			G_EXEC mv CouchPotatoServer-master /etc/couchpotato

			pip2 install -U pip setuptools wheel
			pip2 install -U pyopenssl

		fi

		software_id=143 # Koel
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# APT dependencies: https://docs.koel.dev/#server > https://laravel.com/docs/5.8/installation#server-requirements
			# - python: "gyp verb check python checking for Python executable "python2"/"python" in the PATH"
			# - libpng-dev: "Error: pngquant failed to build, make sure that libpng-dev is installed"
			DEPS_LIST="python libpng-dev $PHP_NAME-bcmath $PHP_NAME-json $PHP_NAME-mbstring $PHP_NAME-xml"

			# Grab latest release
			local fallback_url='https://github.com/koel/koel/releases/download/v5.0.2/koel-v5.0.2.tar.gz'
			Download_Install "$(curl -sSfL 'https://api.github.com/repos/koel/koel/releases/latest' | mawk -F\" '/"browser_download_url": .*\/koel-[^"\/]*\.tar\.gz"/{print $4}')"

			# Reinstall: Clear previous install, but keep existing config file
			if [[ -d '/mnt/dietpi_userdata/koel' ]]; then

				[[ -f '/mnt/dietpi_userdata/koel/.env' ]] && G_EXEC mv /mnt/dietpi_userdata/koel/.env koel-*/
				G_EXEC rm -R /mnt/dietpi_userdata/koel

			fi
			G_EXEC mv koel-* /mnt/dietpi_userdata/koel

			# Download external assets manually, not included in download archive: https://github.com/koel/koel/tree/master/resources
			# - NB: We pull master archive, but this might not match to Koel latest release. Requires testing, else we need to switch to git clone.
			Download_Install 'https://github.com/koel/core/archive/master.tar.gz'
			G_EXEC rmdir /mnt/dietpi_userdata/koel/resources/assets
			G_EXEC mv core-master /mnt/dietpi_userdata/koel/resources/assets

			# Node.js dependencies
			npm i -g --unsafe-perm yarn

			# Download and install composer
			G_EXEC curl -sSfL https://getcomposer.org/composer-stable.phar -o /usr/local/bin/composer
			G_EXEC chmod +x /usr/local/bin/composer

			# Install Koel
			G_EXEC cd /mnt/dietpi_userdata/koel
			composer install
			G_EXEC cd /tmp/$G_PROGRAM_NAME

		fi

		software_id=144 # Sonarr
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# ARMv8 binary install: https://github.com/MichaIng/DietPi/issues/1502
			if (( $G_HW_ARCH == 3 )); then

				# Reinstall: Skip download and install, advice to use internal updater from web UI
				if [[ -d '/opt/NzbDrone' ]]; then

					G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} install dir \"/opt/NzbDrone\" already exists. Download and install steps will be skipped.
 - If you want to update ${aSOFTWARE_NAME[$software_id]}, please use the internal updater from web UI.
 - If you need to reinstall (e.g. broken instance), please manually remove the install dir \"/opt/NzbDrone\" and rerun \"dietpi-software reinstall $software_id\"."

				else

					DEPS_LIST='mediainfo'
					Download_Install 'https://download.sonarr.tv/v2/master/mono/NzbDrone.master.tar.gz' /opt

				fi

			# Repo install
			else

				apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 0xA236C58F409091A18ACA53CBEBFF6B99D9B78493
				echo 'deb https://apt.sonarr.tv/ master main' > /etc/apt/sources.list.d/sonarr.list
				G_AGUP
				G_AGI nzbdrone

			fi

		fi

		software_id=145 # Radarr
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 ))
		then

			Banner_Installing

			# APT dependencies
			DEPS_LIST='mediainfo'
			# - .NET: https://packages.microsoft.com/debian/
			if (( $G_HW_ARCH != 1 ))
			then
				if (( $G_DISTRO < 5 ))
				then
					DEPS_LIST+=' libicu57'

				elif (( $G_DISTRO == 5 ))
				then
					DEPS_LIST+=' libicu63'

				elif (( $G_DISTRO == 6 ))
				then
					DEPS_LIST+=' libicu67'
				fi
			fi

			# Reinstall: Skip download and install, advice to use internal updater from web UI
			if [[ -d '/opt/radarr' ]]
			then
				# shellcheck disable=SC2086
				G_AGI $DEPS_LIST
				DEPS_LIST=
				G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} install dir \"/opt/radarr\" already exists. Download and install steps will be skipped.
 - If you want to update ${aSOFTWARE_NAME[$software_id]}, please use the internal updater from web UI.
 - If you need to reinstall (e.g. broken instance), please manually remove the install dir \"/opt/radarr\" and rerun \"dietpi-software reinstall $software_id\"."

			else
				# ARMv6
				if (( $G_HW_ARCH == 1 ))
				then
					INSTALL_URL_ADDRESS=$(curl -sSfL "$INSTALL_URL_ADDRESS" | mawk -F\" '/"browser_download_url": .*linux\.tar\.gz"/{print $4}')
					local fallback_url='https://github.com/Radarr/Radarr/releases/download/v3.0.2.4552/Radarr.master.3.0.2.4552.linux.tar.gz'

				# ARMv7
				elif (( $G_HW_ARCH == 2 ))
				then
					INSTALL_URL_ADDRESS=$(curl -sSfL "$INSTALL_URL_ADDRESS" | mawk -F\" '/"browser_download_url": .*linux-core-arm\.tar\.gz"/{print $4}')
					local fallback_url='https://github.com/Radarr/Radarr/releases/download/v3.0.2.4552/Radarr.master.3.0.2.4552.linux-core-arm.tar.gz'

				# ARMv8
				elif (( $G_HW_ARCH == 3 ))
				then
					INSTALL_URL_ADDRESS=$(curl -sSfL "$INSTALL_URL_ADDRESS" | mawk -F\" '/"browser_download_url": .*linux-core-arm64\.tar\.gz"/{print $4}')
					local fallback_url='https://github.com/Radarr/Radarr/releases/download/v3.0.2.4552/Radarr.master.3.0.2.4552.linux-core-arm64.tar.gz'

				# x86_64
				elif (( $G_HW_ARCH == 10 ))
				then
					INSTALL_URL_ADDRESS=$(curl -sSfL "$INSTALL_URL_ADDRESS" | mawk -F\" '/"browser_download_url": .*linux-core-x64\.tar\.gz"/{print $4}')
					local fallback_url='https://github.com/Radarr/Radarr/releases/download/v3.0.2.4552/Radarr.master.3.0.2.4552.linux-core-x64.tar.gz'
				fi

				Download_Install "$INSTALL_URL_ADDRESS"
				G_EXEC mv Radarr /opt/radarr
			fi
		fi

		software_id=106 # Lidarr
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Reinstall: Skip download and install, advice to use internal updater from web UI
			if [[ -d '/opt/Lidarr' ]]; then

				G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} install dir \"/opt/Lidarr\" already exists. Download and install steps will be skipped.
 - If you want to update ${aSOFTWARE_NAME[$software_id]}, please use the internal updater from web UI.
 - If you need to reinstall (e.g. broken instance), please manually remove the install dir \"/opt/Lidarr\" and rerun \"dietpi-software reinstall $software_id\"."

			else

				local fallback_url='https://github.com/lidarr/Lidarr/releases/download/v0.7.2.1878/Lidarr.master.0.7.2.1878.linux.tar.gz'
				DEPS_LIST='mediainfo' Download_Install "$(curl -sSfL 'https://api.github.com/repos/Lidarr/Lidarr/releases/latest' | mawk -F\" '/"browser_download_url": .*linux\.tar\.gz"/{print $4}')" /opt

			fi

		fi

		software_id=180 # Bazarr
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing # https://github.com/morpheus65535/bazarr/wiki/Installation#linux

			if [[ -d '/opt/bazarr' ]]; then

				G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} install dir \"/opt/bazarr\" already exists. Download and install steps will be skipped.
 - If you want to update ${aSOFTWARE_NAME[$software_id]}, please use the internal updater from web UI.
 - If you need to reinstall (e.g. broken instance), please manually remove the install dir \"/opt/bazarr\" and rerun \"dietpi-software reinstall $software_id\"."

			else

				INSTALL_URL_ADDRESS='https://github.com/morpheus65535/bazarr.git'
				G_CHECK_URL "$INSTALL_URL_ADDRESS"
				G_EXEC cd /opt
				G_EXEC_OUTPUT=1 G_EXEC git clone "$INSTALL_URL_ADDRESS"

			fi

			# ARM: Install known required Python modules via APT to avoid time-comsuming compiling and large build dependencies (on x86 pre-compiled wheels are available)
			(( $G_HW_ARCH == 10 )) || G_AGI python3-{lxml,numpy}

			G_EXEC cd /opt/bazarr
			G_EXEC_OUTPUT=1 G_EXEC pip3 install -r requirements.txt
			G_EXEC cd /tmp/$G_PROGRAM_NAME

		fi

		software_id=146 # Tautulli
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			if [[ -d '/opt/tautulli' ]]; then

				G_AGI python3-pkg-resources
				G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} install dir \"/opt/tautulli\" already exists. Download and install steps will be skipped.
 - If you want to update ${aSOFTWARE_NAME[$software_id]}, please use the internal updater from WebUI.
 - if you need to reinstall (e.g. broken instance), please manually backup your config files+data, remove the install dir \"/opt/tautulli\" and rerun \"dietpi-software reinstall $software_id\"."

			else

				INSTALL_URL_ADDRESS='https://github.com/Tautulli/Tautulli.git'
				G_CHECK_URL "$INSTALL_URL_ADDRESS"
				G_THREAD_START git clone --depth 1 "$INSTALL_URL_ADDRESS"
				G_AGI python3-pkg-resources
				G_THREAD_WAIT
				G_EXEC mv Tautulli /opt/tautulli

			fi

		fi

		software_id=147 # Jackett
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Grab latest version download link

			# - ARMv6: Requires Mono: https://github.com/Jackett/Jackett#installation-on-linux-armv6-or-below
			INSTALL_URL_ADDRESS=$(curl -sSfL 'https://api.github.com/repos/Jackett/Jackett/releases/latest' | mawk -F\" '/"browser_download_url": .*\/Jackett\.Binaries\.Mono\.tar\.gz"/{print $4}')
			local fallback_url='https://github.com/Jackett/Jackett/releases/download/v0.17.513/Jackett.Binaries.Mono.tar.gz'

			# - ARMv7
			if (( $G_HW_ARCH == 2 )); then

				INSTALL_URL_ADDRESS=${INSTALL_URL_ADDRESS/Mono/LinuxARM32}
				fallback_url=${fallback_url/Mono/LinuxARM32}

			# - ARMv8
			elif (( $G_HW_ARCH == 3 )); then

				INSTALL_URL_ADDRESS=${INSTALL_URL_ADDRESS/Mono/LinuxARM64}
				fallback_url=${fallback_url/Mono/LinuxARM64}

			# - x86_64
			elif (( $G_HW_ARCH == 10 )); then

				INSTALL_URL_ADDRESS=${INSTALL_URL_ADDRESS/Mono/LinuxAMDx64}
				fallback_url=${fallback_url/Mono/LinuxAMDx64}

			fi

			# .NET dependencies: https://packages.microsoft.com/debian/
			if (( $G_HW_ARCH != 1 ))
			then
				DEPS_LIST='liblttng-ust0'
				if (( $G_DISTRO < 5 ))
				then
					DEPS_LIST+=' libicu57'

				elif (( $G_DISTRO == 5 ))
				then
					DEPS_LIST+=' libicu63'

				elif (( $G_DISTRO == 6 ))
				then
					DEPS_LIST+=' libicu67'
				fi
			fi

			Download_Install "$INSTALL_URL_ADDRESS" /opt

			# Move existing configs to unpacked install dir
			[[ -d '/opt/jackett/Jackett' ]] && G_EXEC mv /opt/jackett/Jackett /opt/Jackett/
			[[ -d '/opt/jackett/.mono' ]] && G_EXEC mv /opt/jackett/.mono /opt/Jackett/

			# Remove existing install dir
			[[ -d '/opt/jackett' ]] && G_EXEC rm -R /opt/jackett

			# Move unpacked install dir in place
			G_EXEC mv /opt/Jackett /opt/jackett

		fi

		software_id=149 # NZBGet
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			INSTALL_URL_ADDRESS='https://nzbget.net'
			G_CHECK_URL "$INSTALL_URL_ADDRESS"
			INSTALL_URL_ADDRESS+='/download/nzbget-latest-bin-linux.run'

			G_EXEC curl -sSfL "$INSTALL_URL_ADDRESS" -o package.run
			G_EXEC mkdir -p /mnt/dietpi_userdata/nzbget
			G_EXEC_OUTPUT=1 G_EXEC dash package.run --destdir /mnt/dietpi_userdata/nzbget
			G_EXEC_NOHALT=1 G_EXEC rm package.run

		fi

		software_id=155 # HTPC Manager
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			INSTALL_URL_ADDRESS='https://github.com/HTPC-Manager/HTPC-Manager.git'
			G_CHECK_URL "$INSTALL_URL_ADDRESS"

			# Python build dependencies for ARM
			(( $G_HW_ARCH < 10 )) && G_AGI libffi-dev libssl-dev zlib1g-dev libjpeg-dev

			if [[ -d '/mnt/dietpi_userdata/htpc-manager/.git' ]]; then

				G_EXEC cd /mnt/dietpi_userdata/htpc-manager
				G_EXEC_OUTPUT=1 G_EXEC git remote set-url origin "$INSTALL_URL_ADDRESS"
				G_EXEC_OUTPUT=1 G_EXEC git fetch origin
				G_EXEC_OUTPUT=1 G_EXEC git reset --hard origin
				G_EXEC_OUTPUT=1 G_EXEC git clean -dxfe '/userdata'

			else

				G_EXEC_OUTPUT=1 G_EXEC git clone --depth 1 "$INSTALL_URL_ADDRESS"
				G_EXEC mkdir -p /mnt/dietpi_userdata/htpc-manager
				G_EXEC cp -a HTPC-Manager/. /mnt/dietpi_userdata/htpc-manager/
				G_EXEC_NOHALT=1 G_EXEC rm -R HTPC-Manager
				G_EXEC cd /mnt/dietpi_userdata/htpc-manager
				G_EXEC_OUTPUT=1 G_EXEC git reset --hard origin
				G_EXEC_OUTPUT=1 G_EXEC git clean -dxfe '/userdata'

			fi

			# Python deps
			G_EXEC_OUTPUT=1 G_EXEC pip3 install -Ur requirements.txt

		fi

		software_id=153 # OctoPrint
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 ))
		then
			Banner_Installing

			# Stretch: Install Python 2 development tools since OctoPrint does not support Python 3.5
			local pip='pip3'
			if (( $G_DISTRO < 5 ))
			then
				G_AGI python-{pip,dev}
				G_EXEC_OUTPUT=1 G_EXEC pip2 install -U pip setuptools wheel
				pip='pip2'
			fi

			# Pre-create user and its home directory to allow user-level Python instance
			Create_User -G dialout,tty,video -d /mnt/dietpi_userdata/octoprint octoprint
			[[ -d '/mnt/dietpi_userdata/octoprint' ]] || G_EXEC mkdir -p /mnt/dietpi_userdata/octoprint
			G_EXEC chown -R octoprint:octoprint /mnt/dietpi_userdata/octoprint

			# Clear pip cache in case it got somehow created
			[[ -d '/mnt/dietpi_userdata/octoprint/.cache' ]] && G_EXEC rm -R /mnt/dietpi_userdata/octoprint/.cache

			# Install OctoPrint
			G_EXEC_OUTPUT=1 G_EXEC sudo -u octoprint $pip install -U --no-cache-dir --user octoprint
			unset -v pip
		fi

		software_id=154 # Roon Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing # https://help.roonlabs.com/portal/en/kb/articles/linux-install#Downloads

			DEPS_LIST='cifs-utils'

			# Skip download and install, if already existent, manual removal or internal updater can be used:
			if [[ ! -d '/mnt/dietpi_userdata/roonserver' ]]; then

				Download_Install 'https://download.roonlabs.com/builds/RoonServer_linuxx64.tar.bz2'
				G_EXEC mv RoonServer /mnt/dietpi_userdata/roonserver

			else

				G_AGI $DEPS_LIST
				DEPS_LIST=
				G_DIETPI-NOTIFY 2 "Existing install found\n
The ${aSOFTWARE_NAME[$software_id]} target install directory /mnt/dietpi_userdata/roonserver already exists. This will not be overwritten.\n
If the existing instance is broken or obsolete, please manually backup config files and data, then remove this directory and rerun \"dietpi-software\".\n
If you want to update ${aSOFTWARE_NAME[$software_id]}, please use its internal updater."

			fi

		fi

		software_id=156 # Steam
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Allow non-interactive install
			G_EXEC eval "debconf-set-selections <<< 'steam steam/question select I AGREE'"

			# x86_64: Install Debian i386 package
			if [[ $G_HW_ARCH == 10 ]]
			then
				# Add i386 arch: https://packages.debian.org/bullseye/steam
				[[ $(dpkg --print-foreign-architectures) == *'i386'* ]] || { G_EXEC dpkg --add-architecture i386; G_AGUP; }
				# Add Nvidia i386 driver libs, if Nvidia driver is detected
				local nvidia
				[[ -e '/sys/module/nvidia/version' ]] && nvidia='nvidia-driver-libs:i386'
				G_AGI steam $nvidia

			# ARM: Install repacked Debian i386 package for armhf
			elif [[ $G_HW_ARCH == 2 ]]
			then
				Download_Install "https://dietpi.com/downloads/binaries/$G_DISTRO_NAME/steam_$G_HW_ARCH_NAME.deb"
			fi

		fi

		software_id=158 # MinIO
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Executable
			if [[ $G_HW_ARCH == [12] ]]; then

				INSTALL_URL_ADDRESS='https://dl.minio.io/server/minio/release/linux-arm/minio'

			elif (( $G_HW_ARCH == 3 )); then

				INSTALL_URL_ADDRESS='https://dl.minio.io/server/minio/release/linux-arm64/minio'

			elif (( $G_HW_ARCH == 10 )); then

				INSTALL_URL_ADDRESS='https://dl.minio.io/server/minio/release/linux-amd64/minio'

			fi
			G_CHECK_URL "$INSTALL_URL_ADDRESS"
			[[ -d '/usr/local/bin' ]] || G_EXEC mkdir -p /usr/local/bin
			G_EXEC curl -sSfLo /usr/local/bin/minio "$INSTALL_URL_ADDRESS"
			G_EXEC chmod +x /usr/local/bin/minio

			# Service
			INSTALL_URL_ADDRESS='https://github.com/minio/minio-service/raw/master/linux-systemd/minio.service'
			G_CHECK_URL "$INSTALL_URL_ADDRESS"
			G_EXEC curl -sSfLo /etc/systemd/system/minio.service "$INSTALL_URL_ADDRESS"

			# User
			Create_User -d /mnt/dietpi_userdata/minio-data minio-user

			# Data dir
			G_EXEC mkdir -p /mnt/dietpi_userdata/minio-data
			G_EXEC chown -R minio-user:minio-user /mnt/dietpi_userdata/minio-data

		fi

		software_id=162 # Docker
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Detect distro
			local distro='debian'
			(( $G_HW_MODEL < 10 )) && (( $G_RASPBIAN )) && distro='raspbian'

			# Install APT repo key
			G_EXEC eval "curl -sSfL 'https://download.docker.com/linux/$distro/gpg' | gpg --dearmor -o /etc/apt/trusted.gpg.d/dietpi-docker.gpg --yes"

			# Install APT repo list
			G_EXEC eval "echo 'deb https://download.docker.com/linux/$distro/ ${G_DISTRO_NAME/bullseye/buster} stable' > /etc/apt/sources.list.d/docker.list"
			G_AGUP

			# Install Docker
			G_AGI docker-ce

		fi
		
		software_id=134 # Docker Compose
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Python build dependencies for aarch64
			(( $G_HW_ARCH == 3 )) && G_AGI make gcc

			G_EXEC_OUTPUT=1 G_EXEC pip3 install docker-compose

		fi

		software_id=161 # FuguHub
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			if (( $G_HW_ARCH == 10 )); then

				INSTALL_URL_ADDRESS='https://fuguhub.com/install/FuguHub.linux.install'

			else

				INSTALL_URL_ADDRESS='https://fuguhub.com/releases/raspberrypi/install.sh'

			fi
			Download_Install "$INSTALL_URL_ADDRESS" FuguHubInstall.sh

			G_EXEC chmod +x FuguHubInstall.sh
			G_EXEC_OUTPUT=1 G_EXEC ./FuguHubInstall.sh
			G_EXEC_NOHALT=1 G_EXEC rm FuguHubInstall.sh

			G_EXEC curl -sSfL https://fuguhub.com/box.zip -o /home/bd/applications/box.zip

		fi

		software_id=164 # Nukkit
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			Download_Install 'https://ci.nukkitx.com/job/NukkitX/job/Nukkit/job/master/lastStableBuild/artifact/target/nukkit-1.0-SNAPSHOT.jar' /usr/local/bin/nukkit/nukkit.jar

		fi

		software_id=165 # Gitea
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Binary + data dir
			G_EXEC mkdir -p /mnt/dietpi_userdata/gitea/gitea-repositories

			# ARMv6 + ARMv7 since as of v1.8 there are issues with ARMv7 binaries on Raspbian which are hence not provided anymore: https://github.com/MichaIng/DietPi/issues/2959 (Troubleshooting)
			local arch='arm-6'

			# ARMv8
			if (( $G_HW_ARCH == 3 )); then

				arch='arm64'

			# x86_64
			elif (( $G_HW_ARCH == 10 )); then

				arch='amd64'

			fi

			local fallback_url="https://github.com/go-gitea/gitea/releases/download/v1.13.2/gitea-1.13.2-linux-$arch"
			Download_Install "$(curl -sSfL 'https://api.github.com/repos/go-gitea/gitea/releases/latest' | grep "\"browser_download_url\": .*/gitea-[^\"/]*-linux-$arch\"" | cut -d \" -f 4)" /mnt/dietpi_userdata/gitea/gitea

		fi

		software_id=159 # Allo web UI: 160 for quick reinstall
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 || ${aSOFTWARE_INSTALL_STATE[160]} == 1 )); then

			Banner_Installing
			# Always perform a clean reinstall
			[[ -d '/var/www/allo' ]] && rm -R /var/www/allo
			Download_Install 'https://dietpi.com/downloads/binaries/all/allo_web_interface_v13.2.7z' /var/www

		fi

		software_id=163 # GMediaRender
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# APT dependencies: https://github.com/hzeller/gmrender-resurrect/blob/master/INSTALL.md
			(( $G_DISTRO < 5 )) && DEPS_LIST='libupnp6' || DEPS_LIST='libupnp13'
			DEPS_LIST+=' gstreamer1.0-alsa gstreamer1.0-libav gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly'

			Download_Install "https://dietpi.com/downloads/binaries/$G_DISTRO_NAME/gmediarender_$G_HW_ARCH_NAME.7z" /usr/local/bin
			chmod +x /usr/local/bin/gmediarender

			# Create dummy icons for now. ToDo: Add real icons, GMediaRender or DietPi ones, or disable via build options?
			mkdir -p /usr/local/share/gmediarender
			> /usr/local/share/gmediarender/grender-64x64.png
			> /usr/local/share/gmediarender/grender-128x128.png

		fi

		software_id=166 # Audiophonics Pi-SPC
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Required for systemd-logind, used by dtoverlay=gpio-shutdown,gpio_pin=22,active_low=0
			#	dtoverlay=gpio-poweroff,gpiopin=17
			G_AGI dbus

		fi

		software_id=167 # Raspotify
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing # https://dtcooper.github.io/raspotify/#hard-installation

			INSTALL_URL_ADDRESS='https://dtcooper.github.io/raspotify/key.asc'
			G_CHECK_URL "$INSTALL_URL_ADDRESS"

			curl -sSfL "$INSTALL_URL_ADDRESS" | apt-key add -
			echo 'deb https://dtcooper.github.io/raspotify/ raspotify main' > /etc/apt/sources.list.d/raspotify.list
			G_AGUP

			G_AGI raspotify

		fi

		software_id=169 # Google AIY
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			INSTALL_URL_ADDRESS='https://github.com/google/aiyprojects-raspbian.git'
			G_CHECK_URL "$INSTALL_URL_ADDRESS"

			git clone -b voicekit "$INSTALL_URL_ADDRESS" /mnt/dietpi_userdata/voice-recognizer-raspi
			G_EXEC cd /mnt/dietpi_userdata/voice-recognizer-raspi

			pip3 install -U pip virtualenv
			virtualenv --system-site-packages -p python3 env
			env/bin/pip install -Ur requirements.txt

			#??? ARMv7 only
			(( $G_HW_ARCH == 2 )) && env/bin/pip install google-assistant-library==0.0.3

			# Services
			sed -i 's#/home/pi#/mnt/dietpi_userdata#g' systemd/voice-recognizer.service
			sed -i '/^User=/c\User=dietpi' systemd/voice-recognizer.service

			cp systemd/voice-recognizer.service /etc/systemd/system/
			cp systemd/alsa-init.service /etc/systemd/system/
			#cp systemd/ntpdate.service /etc/systemd/system/

			source env/bin/activate

			# Enable default app for service start
			cp src/assistant_library_with_button_demo.py src/main.py

			G_EXEC cd /tmp/$G_PROGRAM_NAME

		fi

		software_id=176 # Mycroft AI
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Git clone to DietPi userdata
			G_EXEC cd /mnt/dietpi_userdata
			if [[ ! -d 'mycroft-core/.git' ]]; then

				[[ -d 'mycroft-core' ]] && G_EXEC rm -R mycroft-core
				G_THREAD_START git clone -b master --depth 1 https://github.com/MycroftAI/mycroft-core.git

			fi

			# APT deps
			DEPS_LIST='vorbis-tools'

			# Pre-create global config to reflect correct data dir and audio settings within install
			# - Overwrite existing config, user should use /mnt/dietpi_userdata/mycroft-data/.mycroft/mycroft.conf instead to override.
			dps_index=$software_id Download_Install 'mycroft.conf' /etc/mycroft/mycroft.conf

			# Pre-create user and directories with correct permissions
			Create_User -G audio -d /mnt/dietpi_userdata/mycroft-data mycroft
			G_EXEC mkdir -p /mnt/dietpi_userdata/mycroft-data/skills /var/log/mycroft
			G_EXEC chown -R mycroft:mycroft /etc/mycroft /mnt/dietpi_userdata/mycroft-{core,data} /var/log/mycroft
			# - Force skills to be placed directly into data dir
			G_EXEC rm -Rf /opt/mycroft # symlink
			G_EXEC ln -s /mnt/dietpi_userdata/mycroft-data /opt/mycroft
			# - Grant mycroft user sudo permissions which is required for dev_setup.sh and some other Mycroft scripts
			G_EXEC eval "echo 'mycroft ALL=NOPASSWD: ALL' > /etc/sudoers.d/mycroft"

			# Assure 2 GiB overall memory (-100 MiB to avoid tiny swap space) is available
			if (( $RAM_TOTAL < 1948 )); then

				G_DIETPI-NOTIFY 2 '\e[33m[WARNING] Insufficient overall memory size: Mycroft AI requires at least 2 GiB memory on first start. We will now attempt to increase your swap size to satisfy this requirement.'
				/boot/dietpi/func/dietpi-set_swapfile 1

			fi

			# Run installer as user mycroft
			G_EXEC cd /mnt/dietpi_userdata/mycroft-core
			# -sm: Skip mimic build which is time intense and can be manually done any time later.
			# - Remove PulseAudio from dependencies, we use ALSA
			G_EXEC sed -Ei 's/[^[:blank:]"'\'']*pulseaudio[^[:blank:]"'\'']*//g' ./dev_setup.sh
			G_EXEC_OUTPUT=1 G_EXEC sudo -u mycroft ./dev_setup.sh -sm
			G_EXEC cd /tmp/$G_PROGRAM_NAME

		fi

		software_id=86 # Roon Extension Manager
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			INSTALL_URL_ADDRESS='https://github.com/TheAppgineer/roon-extension-manager-packaging/raw/master/linux/setup.sh'
			G_CHECK_URL "$INSTALL_URL_ADDRESS"

			G_EXEC curl -sSfLO "$INSTALL_URL_ADDRESS"
			G_EXEC chmod +x setup.sh
			G_EXEC_OUTPUT=1 G_EXEC ./setup.sh
			G_EXEC_NOHALT=1 G_EXEC rm setup.sh

		fi

		software_id=178 # Jellyfin
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Install APT repo key
			INSTALL_URL_ADDRESS='https://repo.jellyfin.org/jellyfin_team.gpg.key'
			G_CHECK_URL "$INSTALL_URL_ADDRESS"
			G_EXEC eval "curl -sSfL '$INSTALL_URL_ADDRESS' | gpg --dearmor -o /etc/apt/trusted.gpg.d/dietpi-jellyfin.gpg --yes"

			# Install APT repo
			echo "deb https://repo.jellyfin.org/debian/ $G_DISTRO_NAME main" > /etc/apt/sources.list.d/dietpi-jellyfin.list
			G_AGUP

			# Install Jellyfin + FFmpeg implementation
			G_AGI jellyfin jellyfin-ffmpeg

		fi

		software_id=62 # Box86
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 ))
		then
			Banner_Installing

			# APT deps
			DEPS_LIST='cmake python3-minimal'

			# Download
			local version=$(curl -sSfL 'https://api.github.com/repos/ptitSeb/box86/tags' | mawk -F\" '/"name": /{print $4;exit}')
			[[ $version ]] || { version='v0.2.0'; G_DIETPI-NOTIFY 1 "Automatic latest ${aSOFTWARE_NAME[$software_id]} version detection failed. Version \"$version\" will be installed as fallback, but a newer version might be available. Please report this at: https://github.com/MichaIng/DietPi/issues"; }
			Download_Install "https://github.com/ptitSeb/box86/archive/$version.tar.gz"

			# Build
			G_EXEC mkdir box86-${version#v}/build
			G_EXEC cd box86-${version#v}/build
			# - RPi 2
			if [[ $G_HW_MODEL == 2 ]]
			then
				G_EXEC cmake .. -DRPI2=1 -DNOGIT=1 -DCMAKE_BUILD_TYPE=RelWithDebInfo
			# - RPi 3
			elif [[ $G_HW_MODEL == 3 ]]
			then
				G_EXEC cmake .. -DRPI3=1 -DNOGIT=1 -DCMAKE_BUILD_TYPE=RelWithDebInfo
			# - RPi 4
			elif [[ $G_HW_MODEL == 4 ]]
			then
				G_EXEC cmake .. -DRPI4=1 -DNOGIT=1 -DCMAKE_BUILD_TYPE=RelWithDebInfo
			# - Odroids
			elif (( $G_HW_MODEL < 20 ))
			then
				G_EXEC cmake .. -DODROID=1 -DNOGIT=1 -DCMAKE_BUILD_TYPE=RelWithDebInfo
			# - Others
			else
				G_EXEC cmake .. -DARM_DYNAREC=ON -DNOGIT=1 -DCMAKE_BUILD_TYPE=RelWithDebInfo
			fi
			G_EXEC_OUTPUT=1 G_EXEC make CFLAGS='-g0 -O3' "-j$(nproc)"
			G_EXEC strip --remove-section=.comment --remove-section=.note box86
			G_EXEC make install

			# Reload binfmt to have i386 binaries executed via box86 automatically from now on
			(( $G_HW_ARCH < 10 )) && G_EXEC systemctl restart systemd-binfmt
		fi

		software_id=27 # TasmoAdmin
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Install required PHP modules
			DEPS_LIST="$PHP_NAME-curl $PHP_NAME-zip" # https://github.com/reloxx13/TasmoAdmin#linux

			# Skip install, if already present
			if [[ -d '/var/www/tasmoadmin' ]]; then

				G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} install dir \"/var/www/tasmoadmin\" already exists. Download and install steps will be skipped.
 - Please manually backup your config files+data, remove the install dir and rerun \"dietpi-software (re)install $software_id\" if you need to reinstall.
 - If you want to update the ${aSOFTWARE_NAME[$software_id]} instance, please use the internal updater from WebUI."
 				# shellcheck disable=SC2086
				G_AGI $DEPS_LIST
				DEPS_LIST=

			else

				Download_Install 'https://github.com/reloxx13/TasmoAdmin/archive/master.tar.gz'
				mv TasmoAdmin-master/tasmoadmin /var/www/
				rm -R TasmoAdmin-master
				chown -R www-data:www-data /var/www/tasmoadmin

			fi

			# Configure the webserver
			if (( ${aSOFTWARE_INSTALL_STATE[83]} > 0 )); then

				G_DIETPI-NOTIFY 2 'Apache webserver found, enabling TasmoAdmin specific configuration.'
				a2enmod setenvif rewrite authz_core authn_core authn_file 1> /dev/null
				local tasmoadmin_conf='/etc/apache2/sites-available/dietpi-tasmoadmin.conf'
				if [[ -f $tasmoadmin_conf ]]; then

					tasmoadmin_conf+='.dietpi-new'
					G_WHIP_MSG "Existing TasmoAdmin Apache configuration found, will preserve the old one and save the new one for review and comparison to: $tasmoadmin_conf"

				fi
				dps_index=$software_id Download_Install 'apache.tasmoadmin.conf' $tasmoadmin_conf
				a2ensite dietpi-tasmoadmin 1> /dev/null

			elif (( ${aSOFTWARE_INSTALL_STATE[84]} > 0 )); then

				G_DIETPI-NOTIFY 2 'Lighttpd webserver found, enabling TasmoAdmin specific configuration.'
				local tasmoadmin_conf='/etc/lighttpd/conf-available/99-dietpi-tasmoadmin.conf'
				if [[ -f $tasmoadmin_conf ]]; then

					tasmoadmin_conf+='.dietpi-new'
					G_WHIP_MSG "Existing TasmoAdmin Lighttpd configuration found, will preserve the old one and save the new one for review and comparison to: $tasmoadmin_conf"

				fi
				dps_index=$software_id Download_Install 'lighttpd.tasmoadmin.conf' $tasmoadmin_conf
				# Enable required modules + our config
				G_EXEC_POST_FUNC(){ [[ $exit_code == 2 ]] && exit_code=0; } # Do not fail if modules are enabled already
				G_EXEC lighty-enable-mod rewrite dietpi-tasmoadmin

			elif (( ${aSOFTWARE_INSTALL_STATE[85]} > 0 )); then

				G_DIETPI-NOTIFY 2 'Nginx webserver found, enabling TasmoAdmin specific configuration.'
				local tasmoadmin_conf='/etc/nginx/sites-dietpi/dietpi-tasmoadmin.conf'
				if [[ -f $tasmoadmin_conf ]]; then

					owncloud_conf+='.dietpi-new'
					G_WHIP_MSG "Existing TasmoAdmin Nginx configuration found, will preserve the old one and save the new one for review and comparison to: $tasmoadmin_conf"

				fi
				dps_index=$software_id Download_Install 'nginx.tasmoadmin.conf' $tasmoadmin_conf

			fi

		fi

		software_id=157 # Home Assistant
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			local ha_user='homeassistant'
			local ha_home="/home/$ha_user"
			local ha_pyenv_activation=". $ha_home/pyenv-activate.sh"
			local ha_python_version='3.8.8'

			G_DIETPI-NOTIFY 2 "Home Assistant user:  $ha_user"
			G_DIETPI-NOTIFY 2 "Home Assistent home:  $ha_home"
			G_DIETPI-NOTIFY 2 "pyenv activation:     \"$ha_pyenv_activation\""
			G_DIETPI-NOTIFY 2 "pyenv Python version: $ha_python_version"

			# User
			Create_User -G dialout,gpio,i2c -d $ha_home $ha_user

			# Start with fresh instance, to allow clean pyenv and Python updates and fix broken instances. All userdata and configs are preserved in: /mnt/dietpi_userdata/homeassistant
			[[ -d $ha_home/.pyenv ]] && rm -R $ha_home/.pyenv
			[[ -d '/srv/homeassistant' ]] && rm -R /srv/homeassistant # pre-v6-27

			# APT dependencies
			# - MariaDB support: G_AGI libmariadb-dev
			# - Read custom build dependencies
			local custom_apt_deps=$(sed -n '/^[[:blank:]]*SOFTWARE_HOMEASSISTANT_APT_DEPS=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
			DEPS_LIST="gcc libc6-dev make zlib1g-dev libbz2-dev libreadline-dev libssl-dev libsqlite3-dev libffi-dev $custom_apt_deps"
			(( $G_HW_ARCH == 10 )) || DEPS_LIST+=' libjpeg-dev' # Pillow compiling on ARM

			# Install pyenv to $ha_home
			G_EXEC mkdir -p $ha_home
			Download_Install 'https://github.com/pyenv/pyenv/archive/master.tar.gz'
			G_EXEC mv pyenv-master $ha_home/.pyenv
			G_EXEC chown -R $ha_user:$ha_user $ha_home

			# Generate script to activate pyenv: This must be sourced from the originating shell, hence it does not require execute permissions.
			echo "#!/bin/dash
if [ \$(whoami) != '$ha_user' ]; then
	echo '[FAILED] This pyenv must be activated as user \"$ha_user\". Aborting...'
	kill -INT \$\$
fi
cd $ha_home
export PATH=\"$ha_home/.pyenv/bin:\$PATH\"
eval \"\$(pyenv init -)\"" > $ha_home/pyenv-activate.sh

			G_EXEC_DESC='Installing Python with Home Assistant module into pyenv'
			# - Read custom Python modules
			local custom_pip_deps=$(sed -n '/^[[:blank:]]*SOFTWARE_HOMEASSISTANT_PIP_DEPS=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
			# - MariaDB support: pip3 install mysqlclient|PyMySQL
			G_EXEC_OUTPUT=1 G_EXEC sudo -u $ha_user dash -c "
$ha_pyenv_activation
pyenv install $ha_python_version
pyenv local $ha_python_version
pip3 install -U pip wheel
[ -z '$custom_pip_deps' ] || pip3 install $custom_pip_deps
exec pip3 install homeassistant"

			# Generate script to launch HA using pyenv
			echo "#!/bin/dash
$ha_pyenv_activation
exec hass -c '/mnt/dietpi_userdata/homeassistant'" > $ha_home/homeassistant-start.sh
			G_EXEC chmod +x $ha_home/homeassistant-start.sh

			# Generate script to update HA within pyenv
			echo "#!/bin/dash
exec sudo -u $ha_user dash -c '$ha_pyenv_activation; exec pip3 install -U homeassistant'" > $ha_home/homeassistant-update.sh
			G_EXEC chmod +x $ha_home/homeassistant-update.sh

		fi

		software_id=181 # PaperMC
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then
		
			Banner_Installing

			# Make sure user agrees to the EULA
			G_WHIP_BUTTON_OK_TEXT='YES'
			G_WHIP_BUTTON_CANCEL_TEXT='NO'
			if [[ -f '/mnt/dietpi_userdata/papermc/eula.txt' ]] || G_WHIP_YESNO 'Do you agree to the Minecraft EULA found at:\n\nhttps://account.mojang.com/documents/minecraft_eula'
			then
				# Collect latest version of PaperMC
				local url='https://papermc.io/api/v2/projects/paper'
				local version=$(curl -sSfL "$url"); version=${version%\"*} version=${version##*\"}
				local build=$(curl -sSfL "$url/versions/$version"); build=${build%]*} build=${build##*[,[]}
				#local file=$(curl -sSfL "$url/versions/$version/builds/$build"); file=${file##*\"name\":\"} file=${file%%\"*}
				# Download and install PaperMC
				Download_Install "$url/versions/$version/builds/$build/downloads/paper-$version-$build.jar" /opt/papermc/paperclip.jar
				G_EXEC mkdir -p /mnt/dietpi_userdata/papermc
				G_EXEC eval 'echo "eula=true" > /mnt/dietpi_userdata/papermc/eula.txt'
			else
				aSOFTWARE_INSTALL_STATE[$software_id]=0
				G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} install aborted due to outstanding EULA agreement"
			fi
		fi

		software_id=140 # Domoticz
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then
		
			Banner_Installing

			# APT deps
			DEPS_LIST='libusb-0.1 libcurl3-gnutls'

			# Reinstall: Clean old install dir
			[[ -d '/opt/domoticz' ]] && rm -R /opt/domoticz
			Download_Install "https://releases.domoticz.com/releases/release/domoticz_linux_${G_HW_ARCH_NAME/armv6l/armv7l}.tgz" /opt/domoticz

		fi

	}

	Install_Linux_Software(){

		local software_id

		software_id=5 # ALSA
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Get chosen soundcard
			local soundcard=$(sed -n '/^[[:blank:]]*CONFIG_SOUNDCARD=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
			soundcard=${soundcard:-none}

			# Enable defaults, if set to "none"
			if [[ $soundcard == 'none' ]]; then

				# RPi: Onboard auto, Others: default
				(( $G_HW_MODEL < 10 )) && soundcard='rpi-bcm2835-auto' || soundcard='default'

			fi

			# Apply: Installs "alsa-utils"
			/boot/dietpi/func/dietpi-set_hardware soundcard "$soundcard"

		fi

		software_id=126 # LibSSL1.0.0
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# On Stretch and above, libssl1.0.0 is no longer available: https://github.com/MichaIng/DietPi/issues/1299
			# - Our packages are from Debian/Raspbian Jessie, which require multiarch-support: https://packages.debian.org/libssl1.0.0
			# - On Bullseye, multiarch-support is not available, use from Buster instead: https://packages.debian.org/multiarch-support
			# shellcheck disable=SC2015
			(( $G_DISTRO < 6 )) && DEPS_LIST='multiarch-support' || Download_Install "https://dietpi.com/downloads/binaries/all/multiarch-support_$G_HW_ARCH_NAME.deb"
			Download_Install "https://dietpi.com/downloads/binaries/all/libssl1.0.0_$G_HW_ARCH_NAME.deb"

		fi

		software_id=6 # X.Org X server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Generic X server + Mesa OpenGL libraries and utilities
			DEPS_LIST='xserver-xorg-core xserver-xorg-input-libinput xinit dbus-x11 xfonts-base x11-xserver-utils x11-utils libgl1-mesa-dri mesa-utils mesa-utils-extra'

			# On VM, add VMware display driver, which offers slightly better performance. VirtualBox can emulate it as well, which es even the nowadays recommended default.
			(( $G_HW_MODEL == 20 )) && DEPS_LIST+=' xserver-xorg-video-vmware'

			# Disable DPMS and screen blanking
			dps_index=$software_id Download_Install '98-dietpi-disable_dpms.conf' /etc/X11/xorg.conf.d/98-dietpi-disable_dpms.conf

			# RK3399
			if (( $G_HW_CPUID == 3 )); then

				# Buster: Mali-T864 driver by RockChip: https://github.com/rockchip-linux/rk-rootfs-build/tree/master/packages/arm64
				if (( $G_DISTRO < 6 )); then

					Download_Install 'https://dietpi.com/downloads/binaries/rk3399/libmali.deb'
					G_EXEC cd /usr/lib/aarch64-linux-gnu
					ln -sf libMali.so libEGL.so.1.1.0
					ln -sf libMali.so libEGL.so
					ln -sf libMali.so libEGL.so.1.0.0
					ln -sf libMali.so libEGL.so.1.4
					ln -sf libMali.so libEGL.so.1
					ln -sf libMali.so libGLESv2.so
					ln -sf libMali.so libGLESv2.so.2.0
					ln -sf libMali.so libGLESv2.so.2.0.0
					ln -sf libMali.so libGLESv1_CM.so
					ln -sf libMali.so libGLESv1_CM.so.1
					ln -sf libMali.so libGLESv1_CM.so.1.1
					G_EXEC cd /tmp/$G_PROGRAM_NAME

					# X.org config
					G_BACKUP_FP /etc/X11/xorg.conf
					dps_index=$software_id Download_Install 'xorg_rk3399.conf' /etc/X11/xorg.conf

				# Bullseye: Use Mesa drivers provided by Debian repo
				else

					G_AGI libegl1 libgles2

				fi

			# Odroid C2: https://dietpi.com/meveric/pool/main/s/setup-odroid/
			elif (( $G_HW_MODEL == 12 )); then

				DEPS_LIST='mali450-odroid xf86-video-fbturbo-odroid libump-odroid'
				G_BACKUP_FP /etc/X11/xorg.conf
				dps_index=$software_id Download_Install 'xorg_c2.conf' /etc/X11/xorg.conf

			# Odroid XU4: https://dietpi.com/meveric/pool/main/s/setup-odroid/
			elif (( $G_HW_MODEL == 11 )); then

				# xf86-video-armsoc-odroid creates an xorg.conf
				G_AGI firmware-samsung malit628-odroid xf86-video-armsoc-odroid

			# PINE A64
			elif (( $G_HW_MODEL == 40 )); then

				Download_Install 'https://dietpi.com/downloads/binaries/all/libump_1-1_arm64.deb'
				G_EXEC mv /usr/local/lib/libUMP* /usr/lib/
				Download_Install 'https://dietpi.com/downloads/binaries/all/xf86-video-fbturbo_1-1_arm64.deb'
				G_BACKUP_FP /etc/X11/xorg.conf
				dps_index=$software_id Download_Install 'xorg_pine64.conf' /etc/X11/xorg.conf

			# ASUS TB
			elif (( $G_HW_MODEL == 52 )); then

				# Buster: Mali-T760 driver by RockChip: https://github.com/rockchip-linux/rk-rootfs-build/tree/master/packages/armhf
				if (( $G_DISTRO < 6 )); then

					Download_Install 'https://dietpi.com/downloads/binaries/asus/libmali.deb'
					G_EXEC cd /usr/lib/arm-linux-gnueabihf
					ln -sf libMali.so libEGL.so.1.1.0
					ln -sf libMali.so libEGL.so
					ln -sf libMali.so libEGL.so.1.0.0
					ln -sf libMali.so libEGL.so.1.4
					ln -sf libMali.so libEGL.so.1
					ln -sf libMali.so libGLESv2.so
					ln -sf libMali.so libGLESv2.so.2.0
					ln -sf libMali.so libGLESv2.so.2.0.0
					ln -sf libMali.so libGLESv1_CM.so
					ln -sf libMali.so libGLESv1_CM.so.1
					ln -sf libMali.so libGLESv1_CM.so.1.1
					G_EXEC cd /tmp/$G_PROGRAM_NAME

					# X.org config
					G_BACKUP_FP /etc/X11/xorg.conf
					dps_index=$software_id Download_Install 'xorg_asustb.conf' /etc/X11/xorg.conf

				# Bullseye: Use Mesa drivers provided by Debian repo
				else

					G_AGI libegl1 libgles2

				fi

			fi

		fi

		software_id=151 # Nvidia driver
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI nvidia-driver

		fi

		software_id=152 # Avahi-Daemon
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI avahi-daemon

		fi

		software_id=16 # Build essentials
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI build-essential automake

		fi

		software_id=100 # PiJuice
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI pijuice-base

		fi

		software_id=17 # Git Client
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI git

		fi

		software_id=4 # Vifm
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI vifm

		fi

		software_id=20
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI vim

		fi

		software_id=21
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI vim-tiny

		fi

		software_id=127 # Neovim
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI neovim

		fi

		software_id=18
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI emacs

		fi

		software_id=12
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI iperf

		fi

		software_id=3 # MC
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI mc

		fi

		software_id=19
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI jed

		fi

		software_id=10
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI iftop

		fi

		software_id=11
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI iptraf

		fi

		software_id=13
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI mtr-tiny

		fi

		software_id=14
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI nload

		fi

		software_id=15
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI tcpdump

		fi

		software_id=0 # OpenSSH Client
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI openssh-client

		fi

		software_id=1 # Samba Client
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Remove Information file
			[[ -f '/mnt/samba/readme.txt' ]] && G_EXEC rm /mnt/samba/readme.txt

			G_AGI smbclient cifs-utils

		fi

		software_id=110 # NFS Client
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Remove information file
			[[ -f '/mnt/nfs_client/readme.txt' ]] && G_EXEC rm /mnt/nfs_client/readme.txt

			# "netbase" is needed for mounting NFSv3: https://github.com/MichaIng/DietPi/issues/1898#issuecomment-406247814
			G_AGI nfs-common netbase

		fi

		software_id=104 # Dropbear
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Mark target SSH server choice, if not selected via menu or first run setup
			INDEX_SSHSERVER_TARGET=-1

			# Stop OpenSSH service to unbind port 22
			systemctl -q is-active ssh && G_EXEC systemctl stop ssh

			# On Stretch + Buster Dropbear packages have been split, install "dropbear-run" only, to have active service, but skip "dropbear-initramfs"
			# On Bullseye+ "dropbear-run" has become a transitional dummy package for "dropbear" which does not include "dropbear-initramfs" anymore.
			if (( $G_DISTRO > 5 )); then

				G_AGI dropbear

			else

				G_AGI dropbear-run

			fi

			# Enable Dropbear daemon
			G_CONFIG_INJECT 'NO_START=' 'NO_START=0' /etc/default/dropbear

			# Failsafe: Enable Dropbear service
			G_EXEC systemctl enable dropbear
			aSTART_SERVICES+=('dropbear')

			# Mark OpenSSH for uninstall and update choice system
			dpkg-query -s 'openssh-server' &> /dev/null && aSOFTWARE_INSTALL_STATE[105]=-1 && UNINSTALL_REQUIRED=1
			INDEX_SSHSERVER_CURRENT=-1

		fi

		software_id=105 # OpenSSH Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Mark target SSH server choice, if not selected via menu or first run setup
			INDEX_SSHSERVER_TARGET=-2

			# Stop Dropbear service to unbind port 22
			systemctl -q is-active dropbear && G_EXEC systemctl stop dropbear

			# SSH server package pulls client as dependency. Mark client hence as installed and mark it explicitly so that it is not autoremoved with the server but must be marked for uninstall instead.
			G_AGI openssh-server openssh-client
			aSOFTWARE_INSTALL_STATE[0]=2

			# Allow root login
			G_CONFIG_INJECT 'PermitRootLogin[[:blank:]]' 'PermitRootLogin yes' /etc/ssh/sshd_config

			# Reload SSH server now so root users can login during setup.
			systemctl reload ssh

			# Failsafe: Enable OpenSSH service
			G_EXEC systemctl enable ssh
			aSTART_SERVICES+=('ssh')

			# Mark Dropbear for uninstall and update choice system
			grep -q '^dropbear[^[:blank:]]*[[:blank:]]' <<< "$(dpkg --get-selections)" && aSOFTWARE_INSTALL_STATE[104]=-1 && UNINSTALL_REQUIRED=1
			INDEX_SSHSERVER_CURRENT=-2

		fi

		software_id=103 # DietPi-RAMlog
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Install persistent tmpfs
			local tmpfs_max_size=$(sed -n '/^[[:blank:]]*AUTO_SETUP_RAMLOG_MAXSIZE=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
			sed -i '/[[:blank:]]\/var\/log[[:blank:]]/d' /etc/fstab
			echo "tmpfs /var/log tmpfs size=${tmpfs_max_size:-50}M,noatime,lazytime,nodev,nosuid,mode=1777" >> /etc/fstab

			# Enable DietPi-RAMdisk
			G_EXEC systemctl enable dietpi-ramlog

			# To assure a cleaned mountpoint we need to start RAMlog now
			local acommand=('/boot/dietpi/func/dietpi-ramlog' '1')
			systemctl is-active dietpi-ramlog > /dev/null && acommand=('systemctl' 'stop' 'dietpi-ramlog')
			G_EXEC_DESC='Storing /var/log metadata to disk' G_EXEC "${acommand[@]}"
			unset -v acommand
			findmnt /var/log > /dev/null && G_EXEC_DESC='Unmounting /var/log' G_EXEC_NOHALT=1 G_EXEC umount -R /var/log
			G_EXEC_DESC='Cleaning /var/log mountpoint' G_EXEC rm -Rf /var/log/{,.??,.[^.]}*
			G_EXEC_DESC='Mounting tmpfs to /var/log' G_EXEC mount /var/log
			G_EXEC_DESC='Restoring metadata to /var/log tmpfs' G_EXEC systemctl start dietpi-ramlog

		fi

		software_id=101 # Logrotate
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI logrotate

		fi

		software_id=102 # Rsyslog
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Workaround for dpkg failure on 1st install if service is already running but APT not installed: https://github.com/MichaIng/DietPi/pull/2277/#issuecomment-441460925
			systemctl stop rsyslog 2> /dev/null
			G_AGI rsyslog
			G_EXEC systemctl enable --now rsyslog

		fi

		software_id=7 # FFmpeg
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing
			G_AGI ffmpeg

		fi

		software_id=8 # Java
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# On Stretch and ARMv6 (RPi 1/Zero) use Java 8, else most current
			# Stretch does not ship Java 11 yet, ARMv6 is not Java 11 compatible, but luckily Raspbian ships Java 8 even on Bullseye: https://github.com/MichaIng/DietPi/issues/3182
			local version=11
			(( $G_DISTRO < 5 || G_HW_ARCH == 1 )) && version=8

			# Allow two attempts as workaround for ARM install issue: https://github.com/MichaIng/DietPi/issues/2524
			G_EXEC_RETRIES=1 G_AGI ca-certificates-java openjdk-$version-jre-headless openjdk-$version-jdk-headless

		fi

		software_id=9 # Node.js
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			INSTALL_URL_ADDRESS='https://raw.githubusercontent.com/MichaIng/nodejs-linux-installer/master/node-install.sh'
			G_CHECK_URL "$INSTALL_URL_ADDRESS"
			G_EXEC curl -sSfLO "$INSTALL_URL_ADDRESS"
			G_EXEC chmod +x node-install.sh
			G_EXEC mkdir -p /usr/local # failsafe
			G_EXEC_OUTPUT=1 G_EXEC ./node-install.sh
			G_EXEC_NOHALT=1 G_EXEC rm node-install.sh

			# Deps: https://github.com/MichaIng/DietPi/issues/3614
			G_AGI libatomic1

		fi

		software_id=130 # Python 3 pip
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# Create piwheels config file for ARMv6 and ARMv7
			[[ $G_HW_ARCH != [12] || -f '/etc/pip.conf' ]] || G_EXEC eval "echo -e '[global]\nextra-index-url=https://www.piwheels.org/simple/' > /etc/pip.conf"

			# Perform pip3 installation
			INSTALL_URL_ADDRESS='https://bootstrap.pypa.io/get-pip.py'
			(( $G_DISTRO > 4 )) || INSTALL_URL_ADDRESS='https://bootstrap.pypa.io/3.5/get-pip.py' # https://pip.pypa.io/en/stable/news/#id1
			DEPS_LIST='python3-dev' Download_Install "$INSTALL_URL_ADDRESS"
			G_EXEC_OUTPUT=1 G_EXEC python3 ./get-pip.py
			G_EXEC_NOHALT=1 G_EXEC rm get-pip.py

		fi

		software_id=150 # Mono runtime
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing # https://www.mono-project.com/download/stable/#download-lin-debian

			apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF

			# On RPi use separate Raspbian repo: https://github.com/MichaIng/DietPi/issues/1023
			# Use Buster branch on Bullseye
			if (( $G_HW_MODEL < 10 )) && (( $G_RASPBIAN )); then

				echo "deb https://download.mono-project.com/repo/debian/ raspbian${G_DISTRO_NAME/bullseye/buster} main" > /etc/apt/sources.list.d/mono-xamarin.list

			else

				echo "deb https://download.mono-project.com/repo/debian/ ${G_DISTRO_NAME/bullseye/buster} main" > /etc/apt/sources.list.d/mono-xamarin.list

			fi

			G_AGUP
			G_AGI mono-runtime mono-complete

			rm -f /tmp/mono* # https://github.com/MichaIng/DietPi/issues/1877#issuecomment-403856446

		fi

		software_id=170 # UnRAR
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Installing

			# On Raspbian, only "unrar-free" is available in repos, but does not support all rar formats, thus we use "unrar" [non-free] from Debian repo: http://raspbian.raspberrypi.org/raspbian/pool/non-free/u/unrar-nonfree/
			if (( $G_HW_MODEL < 10 )) && (( $G_RASPBIAN )); then

				Download_Install "https://dietpi.com/downloads/binaries/rpi/unrar-armhf-$G_DISTRO_NAME.deb"

			else

				G_AGI unrar

			fi

		fi

	}

	Apply_SSHServer_Choices(){

		# Work out which SSH server needs installing from IDs (if any)
		# Work out which SSH server needs removing (if any)
		if (( $INDEX_SSHSERVER_TARGET != $INDEX_SSHSERVER_CURRENT )); then

			# No SSH server
			if (( $INDEX_SSHSERVER_TARGET == 0 )); then

				# Check currently installed SSH servers via "dpkg --get-selections" to be failsafe
				local dpkg_out=$(dpkg --get-selections)

				grep -q '^dropbear[^[:blank:]]*[[:blank:]]' <<< "$dpkg_out" && aSOFTWARE_INSTALL_STATE[104]=-1 UNINSTALL_REQUIRED=1
				grep -q '^openssh-server[[:blank:]]' <<< "$dpkg_out" && aSOFTWARE_INSTALL_STATE[105]=-1 UNINSTALL_REQUIRED=1

			# Dropbear
			elif (( $INDEX_SSHSERVER_TARGET == -1 )); then

				aSOFTWARE_INSTALL_STATE[104]=1

			# OpenSSH
			elif (( $INDEX_SSHSERVER_TARGET == -2 )); then

				aSOFTWARE_INSTALL_STATE[105]=1

			fi

		fi

	}

	Apply_FileServer_Choices(){

		# Work out which file Server needs installing from IDs (if any)
		# Work out which file server needs removing (if any)
		if (( $INDEX_FILESERVER_TARGET != $INDEX_FILESERVER_CURRENT )); then

			# No file server
			if (( $INDEX_FILESERVER_TARGET == 0 )); then

				(( ${aSOFTWARE_INSTALL_STATE[94]} == 2 )) && aSOFTWARE_INSTALL_STATE[94]=-1 UNINSTALL_REQUIRED=1
				(( ${aSOFTWARE_INSTALL_STATE[96]} == 2 )) && aSOFTWARE_INSTALL_STATE[96]=-1 UNINSTALL_REQUIRED=1

			# ProFTPD
			elif (( $INDEX_FILESERVER_TARGET == -1 )); then

				aSOFTWARE_INSTALL_STATE[94]=1
				(( ${aSOFTWARE_INSTALL_STATE[96]} == 2 )) && aSOFTWARE_INSTALL_STATE[96]=-1 UNINSTALL_REQUIRED=1

			# Samba
			elif (( $INDEX_FILESERVER_TARGET == -2 )); then

				aSOFTWARE_INSTALL_STATE[96]=1
				(( ${aSOFTWARE_INSTALL_STATE[94]} == 2 )) && aSOFTWARE_INSTALL_STATE[94]=-1 UNINSTALL_REQUIRED=1

			fi

			# Update current file server index
			INDEX_FILESERVER_CURRENT=$INDEX_FILESERVER_TARGET

		fi

	}

	Apply_Logging_Choices(){

		# Work out which logging system needs installing from IDs (if any)
		# Work out which logging system needs removing (if any)
		if (( $INDEX_LOGGING_TARGET != $INDEX_LOGGING_CURRENT )); then

			# None
			if (( $INDEX_LOGGING_TARGET == 0 )); then

				(( ${aSOFTWARE_INSTALL_STATE[101]} == 2 )) && aSOFTWARE_INSTALL_STATE[101]=-1 UNINSTALL_REQUIRED=1
				(( ${aSOFTWARE_INSTALL_STATE[102]} == 2 )) && aSOFTWARE_INSTALL_STATE[102]=-1 UNINSTALL_REQUIRED=1
				(( ${aSOFTWARE_INSTALL_STATE[103]} == 2 )) && aSOFTWARE_INSTALL_STATE[103]=-1 UNINSTALL_REQUIRED=1

			# RAMlog - clear every hour
			elif (( $INDEX_LOGGING_TARGET == -1 )); then

				aSOFTWARE_INSTALL_STATE[103]=1
				(( ${aSOFTWARE_INSTALL_STATE[101]} == 2 )) && aSOFTWARE_INSTALL_STATE[101]=-1 UNINSTALL_REQUIRED=1
				(( ${aSOFTWARE_INSTALL_STATE[102]} == 2 )) && aSOFTWARE_INSTALL_STATE[102]=-1 UNINSTALL_REQUIRED=1

			# RAMlog - backup every hour to /root/logfile_storage, then clear
			elif (( $INDEX_LOGGING_TARGET == -2 )); then

				aSOFTWARE_INSTALL_STATE[103]=1
				(( ${aSOFTWARE_INSTALL_STATE[101]} == 2 )) && aSOFTWARE_INSTALL_STATE[101]=-1 UNINSTALL_REQUIRED=1
				(( ${aSOFTWARE_INSTALL_STATE[102]} == 2 )) && aSOFTWARE_INSTALL_STATE[102]=-1 UNINSTALL_REQUIRED=1

			# Logrotate + Rsyslog - logs to disk
			elif (( $INDEX_LOGGING_TARGET == -3 )); then

				aSOFTWARE_INSTALL_STATE[101]=1
				aSOFTWARE_INSTALL_STATE[102]=1
				(( ${aSOFTWARE_INSTALL_STATE[103]} == 2 )) && aSOFTWARE_INSTALL_STATE[103]=-1 UNINSTALL_REQUIRED=1

			fi

			# Update current logging index
			INDEX_LOGGING_CURRENT=$INDEX_LOGGING_TARGET

		fi

		# Reinstall DietPi-RAMlog on firstrun installs, if enabled, to apply AUTO_SETUP_RAMLOG_MAXSIZE
		(( $G_DIETPI_INSTALL_STAGE == 1 && ${aSOFTWARE_INSTALL_STATE[103]} == 2 )) && aSOFTWARE_INSTALL_STATE[103]=1

	}

	Apply_Webserver_Preference()
	{
		# Update current webserver index
		INDEX_WEBSERVER_CURRENT=$INDEX_WEBSERVER_TARGET
	}

	# Configure marked software titles
	Install_Apply_Configs(){

		local software_id

		software_id=23 # LXDE
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Openbox + PCmanFM + panel configs
			G_EXEC mkdir -p /etc/xdg/{openbox/LXDE,pcmanfm/LXDE,lxpanel/LXDE/panels}
			G_THREAD_START curl -sSfL "https://raw.githubusercontent.com/$G_GITOWNER/DietPi/$G_GITBRANCH/.conf/desktop/lxde/lxde-rc.xml" -o /etc/xdg/openbox/LXDE/rc.xml
			G_THREAD_START curl -sSfL "https://raw.githubusercontent.com/$G_GITOWNER/DietPi/$G_GITBRANCH/.conf/desktop/lxde/pcmanfm.conf" -o /etc/xdg/pcmanfm/LXDE/pcmanfm.conf
			G_THREAD_START curl -sSfL "https://raw.githubusercontent.com/$G_GITOWNER/DietPi/$G_GITBRANCH/.conf/desktop/lxde/panel" -o /etc/xdg/lxpanel/LXDE/panels/panel
			
			# Remove LXRandR menu item (monitor configuration tool as we set res in dietpi-config)
			[[ -f '/usr/share/applications/lxrandr.desktop' ]] && G_EXEC rm /usr/share/applications/lxrandr.desktop

			# Disable trash
			G_CONFIG_INJECT 'use_trash=' 'use_trash=0' /etc/xdg/libfm/libfm.conf '\[config\]'

			# Execute desktop icons and executable files directly without asking what to do
			G_CONFIG_INJECT 'quick_exec=' 'quick_exec=1' /etc/xdg/libfm/libfm.conf '\[config\]'

			# Firefox optimisations
			#	??? These keep adding new entries, ignore existing entry???
			#G_CONFIG_INJECT 'extensions.update.enabled' 'pref("extensions.update.enabled", false);' /etc/firefox-esr/firefox-esr.js
			#G_CONFIG_INJECT 'datareporting.healthreport.uploadEnabled' 'pref("datareporting.healthreport.uploadEnabled", false);' /etc/firefox-esr/firefox-esr.js
			#G_CONFIG_INJECT 'browser.cache.disk.parent_directory' 'pref("browser.cache.disk.parent_directory", "/tmp/firefox_cache");' /etc/firefox-esr/firefox-esr.js
			#G_CONFIG_INJECT 'general.smoothScroll' 'pref("general.smoothScroll", false);' /etc/firefox-esr/firefox-esr.js

			Create_Desktop_Shared_Items

			# Composition manager: Enable limited server-side composition
			dps_index=6 Download_Install 'xcompmgr.desktop' /etc/xdg/autostart/xcompmgr.desktop

		fi

		software_id=24 # MATE
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			Create_Desktop_Shared_Items
			
			# Odroid C2, define default pulseaudio sink: https://github.com/MichaIng/DietPi/issues/415
			[[ $G_HW_MODEL == 12 && -f '/etc/pulse/default.pa' ]] && G_CONFIG_INJECT 'set-default-sink[[:blank:]]' 'set-default-sink alsa_output.platform-odroid_hdmi.37.analog-stereo' /etc/pulse/default.pa

		fi

		software_id=173 # LXQt
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Configs
			Download_Install "https://github.com/$G_GITOWNER/DietPi/raw/$G_GITBRANCH/.conf/desktop/lxqt/lxqt-$G_DISTRO_NAME.7z" /etc/xdg/
			
			# Disable trash
			# - Skip on Buster since trash seems to be implemented differently: https://github.com/MichaIng/DietPi/issues/1918#issuecomment-488085982
			(( $G_DISTRO < 5 )) && G_CONFIG_INJECT 'use_trash=' 'use_trash=0' /etc/xdg/libfm/libfm.conf

			Create_Desktop_Shared_Items

			# Composition manager: LXQt has one integrated with xfwm4, which is blocked by xcompmgr, breaking settings and leading to ugly results: https://github.com/MichaIng/DietPi/issues/3665
			[[ -f '/etc/xdg/autostart/xcompmgr.desktop' ]] && G_EXEC rm /etc/xdg/autostart/xcompmgr.desktop

		fi

		software_id=26 # GNUstep
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration
			Create_Desktop_Shared_Items

		fi

		software_id=25 # Xfce
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration
			Create_Desktop_Shared_Items

			# Composition manager: Xfce has one integrated with xfwm4, which is blocked by xcompmgr, breaking settings and leading to ugly results: https://github.com/MichaIng/DietPi/issues/3665
			[[ -f '/etc/xdg/autostart/xcompmgr.desktop' ]] && G_EXEC rm /etc/xdg/autostart/xcompmgr.desktop

		fi

		software_id=83 # Apache
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Config
			G_BACKUP_FP /etc/apache2/apache2.conf
			dps_index=$software_id Download_Install 'apache2.conf' /etc/apache2/apache2.conf
			cat << '_EOF_' > /etc/apache2/sites-available/000-default.conf
<VirtualHost *:80>
	DocumentRoot /var/www
	ErrorLog ${APACHE_LOG_DIR}/error.log
	#CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
_EOF_
			cat << _EOF_ > /etc/apache2/mods-available/mpm_prefork.conf
<IfModule mpm_prefork_module>
	StartServers		$G_HW_CPU_CORES
	MinSpareServers		1
	MaxSpareServers		$G_HW_CPU_CORES
	MaxRequestWorkers	50
	MaxConnectionsPerChild	0
</IfModule>
_EOF_
			# Webroot
			[[ -f '/var/www/html/index.html' ]] && G_EXEC mv /var/www/html/index.html /var/www/
			[[ -d '/var/www/html' ]] && G_EXEC rmdir --ignore-fail-on-non-empty /var/www/html

			# Change error log level
			G_EXEC sed -i '/LogLevel[[:blank:]]/c\	LogLevel error' /etc/apache2/sites-available/*

		fi

		software_id=85 # Nginx
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Custom confs, included by sites-enabled/default within server directive, while nginx/(conf.d|sites-enabled) is included by nginx.conf outside server directive
			G_EXEC mkdir -p /etc/nginx/sites-dietpi

			G_BACKUP_FP /etc/nginx/nginx.conf
			dps_index=$software_id Download_Install 'nginx.conf' /etc/nginx/nginx.conf
			# Adjust socket name to PHP version
			G_EXEC sed -i "s#/run/php/php.*-fpm.sock#/run/php/$PHP_NAME-fpm.sock#g" /etc/nginx/nginx.conf

			# CPU core count
			G_EXEC sed -i "/worker_processes/c\worker_processes $G_HW_CPU_CORES;" /etc/nginx/nginx.conf

			# Default site
			dps_index=$software_id Download_Install 'nginx.default' /etc/nginx/sites-available/default

			# Webroot
			[[ -f '/var/www/html/index.nginx-debian.html' ]] && G_EXEC mv /var/www/html/index.nginx-debian.html /var/www/
			[[ -d '/var/www/html' ]] && G_EXEC rmdir --ignore-fail-on-non-empty /var/www/html

		fi

		software_id=84 # Lighttpd
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Remove obsolete sysvinit service
			[[ -f '/etc/init.d/lighttpd' ]] && rm /etc/init.d/lighttpd
			update-rc.d -f lighttpd remove

			# Webroot
			G_CONFIG_INJECT 'server.document-root' 'server.document-root = "/var/www"' /etc/lighttpd/lighttpd.conf
			# - Move default page
			[[ -f '/var/www/html/index.lighttpd.html' ]] && G_EXEC mv /var/www/html/index.lighttpd.html /var/www/
			[[ -d '/var/www/html' ]] && G_EXEC rmdir --ignore-fail-on-non-empty /var/www/html

			# Fix in case Lighttpd got updated to Buster version (on ARMv6 due to PHP7.3 libssl1.1 depedency)
			# - "create-mime.assign.pl" has been renamed to "create-mime.conf.pl"
			[[ -f '/usr/share/lighttpd/create-mime.assign.pl' ]] || G_EXEC sed -i 's/create-mime\.assign\.pl/create-mime.conf.pl/g' /etc/lighttpd/lighttpd.conf

			# Configure fastcgi for PHP-FPM
			cat << _EOF_ > /etc/lighttpd/conf-available/15-fastcgi-php.conf
# -*- depends: fastcgi -*-
# /usr/share/doc/lighttpd/fastcgi.txt.gz
# https://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ConfigurationOptions#mod_fastcgi-fastcgi

## Start an FastCGI server using php-fpm
fastcgi.server += ( ".php" =>
        ((
                "socket" => "/run/php/${PHP_NAME}-fpm.sock",
                "broken-scriptfilename" => "enable"
        ))
)
_EOF_
			# - Enable modules
			G_EXEC_POST_FUNC(){ [[ $exit_code == 2 ]] && exit_code=0; } # Do not fail if modules are enabled already
			G_EXEC lighty-enable-mod fastcgi fastcgi-php

		fi

		software_id=89 # PHP
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# PHP-FPM
			if (( ${aSOFTWARE_INSTALL_STATE[84]} > 0 || ${aSOFTWARE_INSTALL_STATE[85]} > 0 )); then

				# Base
				sed -i '/cgi.fix_pathinfo=/c\cgi.fix_pathinfo=1' "$FP_PHP_BASE_DIR"/fpm/php.ini

				# Optimisations based on total cores
				sed -i "/pm.max_children = /c\pm.max_children = $(( $G_HW_CPU_CORES * 3 ))" "$FP_PHP_BASE_DIR"/fpm/pool.d/www.conf
				sed -i "/pm.start_servers = /c\pm.start_servers = $G_HW_CPU_CORES" "$FP_PHP_BASE_DIR"/fpm/pool.d/www.conf
				sed -i "/pm.min_spare_servers = /c\pm.min_spare_servers = $G_HW_CPU_CORES" "$FP_PHP_BASE_DIR"/fpm/pool.d/www.conf
				sed -i "/pm.max_spare_servers = /c\pm.max_spare_servers = $G_HW_CPU_CORES" "$FP_PHP_BASE_DIR"/fpm/pool.d/www.conf

				# Enviroment settings
				sed -i "/env\[HOSTNAME\]/c\env\[HOSTNAME\] = \$HOSTNAME" "$FP_PHP_BASE_DIR"/fpm/pool.d/www.conf
				sed -i "/env\[PATH\]/c\env\[PATH\] = /usr/local/bin:/usr/bin:/bin" "$FP_PHP_BASE_DIR"/fpm/pool.d/www.conf

			fi

			# We create our own PHP mod to add DietPi specific configs.
			target_php_ini="$FP_PHP_BASE_DIR/mods-available/dietpi.ini"
			echo -e '; DietPi PHP settings\n; priority=97' > $target_php_ini

			# Set UTF-8
			G_CONFIG_INJECT 'default_charset[[:blank:]]*=' 'default_charset="UTF-8"' $target_php_ini

			# Session files need to be outside of /tmp and /var/tmp due to PrivateTmp=true, else phpsessionclean.service cannot clean sessions
			G_EXEC mkdir -p /run/php_sessions
			G_EXEC chmod 1733 /run/php_sessions
			echo -e '# Pre-create PHP sessions dir\nd /run/php_sessions 1733' > /etc/tmpfiles.d/dietpi-php_sessions.conf
			G_CONFIG_INJECT 'session.save_path[[:blank:]]*=' session.save_path="/run/php_sessions" $target_php_ini

			# File uploads: https://github.com/MichaIng/DietPi/issues/546
			# - This is especially relevant for cloud software like ownCloud/Nextcloud.
			# - Generally we want to hold tmp upload files in RAM to reduce disk (especially SD card) writes for performance and disk wear reasons.
			# - By default only max 2 MiB file uploads are allowed, hold in /tmp tmpfs, which is safe but not usable for usual cloud usage.
			# - ownCloud/Nextcloud do/did override this limit to 512 MiB, a reasonable limit which can usually still be hold in RAM without issues.
			# - Low RAM devices (RPi1 256 MiB model) require a swap file for this, however, it is still better to cause disk writes through swap file during large file uploads only, then doing this for each and every uploaded file.
			# - When larger file uploads are required, it depends on the system total RAM, rootfs disk and available external drives if/where to move tmp file uploads, resize or move swap file. This should be then left to user.
			G_CONFIG_INJECT 'upload_tmp_dir=[[:blank:]]*' 'upload_tmp_dir=/tmp' $target_php_ini
			G_CONFIG_INJECT 'upload_max_filesize[[:blank:]]*=' 'upload_max_filesize=512M' $target_php_ini
			G_CONFIG_INJECT 'post_max_size[[:blank:]]*=' 'post_max_size=512M' $target_php_ini
			# - Nginx: https://github.com/MichaIng/DietPi/issues/546 => https://github.com/MichaIng/DietPi/blob/dev/.conf/dps_85/nginx.conf

			# Cache settings
			local target_php_cachesize=$(( $RAM_PHYS / 30 ))
			(( $target_php_cachesize < 10 )) && target_php_cachesize=10
			# - OPcache
			G_CONFIG_INJECT 'opcache.enable[[:blank:]]*=' 'opcache.enable=1' $target_php_ini
			G_CONFIG_INJECT 'opcache.memory_consumption[[:blank:]]*=' "opcache.memory_consumption=$target_php_cachesize" $target_php_ini
			#	Assure that interned_strings_buffer is never larger than hald of memory_consumption: https://github.com/MichaIng/DietPi/issues/2293
			(( $($PHP_NAME -i | mawk '/^opcache.interned_strings_buffer/{print $5;exit}') > $target_php_cachesize / 2 )) && G_CONFIG_INJECT 'opcache.interned_strings_buffer[[:blank:]]*=' "opcache.interned_strings_buffer=$(( $target_php_cachesize / 2 ))" $target_php_ini
			G_CONFIG_INJECT 'opcache.revalidate_freq[[:blank:]]*=' 'opcache.revalidate_freq=60' $target_php_ini # 1 minute
			# - APCu
			G_CONFIG_INJECT 'apc.shm_size[[:blank:]]*=' "apc.shm_size=${target_php_cachesize}M" $target_php_ini
			G_CONFIG_INJECT 'apc.ttl[[:blank:]]*=' 'apc.ttl=259200' $target_php_ini # 3 days

			# Enable all available PHP modules
			local amodules=()
			mapfile -t amodules < <(find "$FP_PHP_BASE_DIR/mods-available" -type f -name '*.ini' -printf '%f\n')
			G_EXEC phpenmod "${amodules[@]%.ini}"
			unset -v amodules

			# Create PHP info pages within webroot if webserver is installed
			if (( ${aSOFTWARE_INSTALL_STATE[83]} > 0 || ${aSOFTWARE_INSTALL_STATE[84]} > 0 || ${aSOFTWARE_INSTALL_STATE[85]} > 0 )); then

				# PHP info page
				echo '<?php phpinfo(); ?>' > /var/www/phpinfo.php
				# OPcache info page
				G_THREAD_START curl -sSfL https://raw.githubusercontent.com/amnuts/opcache-gui/master/index.php -o /var/www/opcache.php
				# APCu info page
				G_THREAD_START curl -sSfL https://raw.githubusercontent.com/krakjoe/apcu/master/apc.php -o /var/www/apc.php

				G_THREAD_WAIT

			fi

		fi

		software_id=88 # MariaDB
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			G_DIETPI-NOTIFY 2 'Assuring 4-byte support and InnoDB Barracuda file format with large prefix'
			cat << _EOF_ > /etc/mysql/mariadb.conf.d/99-dietpi-4byte.cnf
[mysqld]
innodb_large_prefix=1
innodb_file_format=barracuda
innodb_file_per_table=1
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci
_EOF_
			# Since Buster (MariaDB 10.3) the following settings are removed and default to our needs:
			# - innodb_large_prefix: https://mariadb.com/kb/en/library/innodb-system-variables/#innodb_large_prefix
			# - innodb_file_format: https://mariadb.com/kb/en/library/innodb-system-variables/#innodb_file_format
			if (( $G_DISTRO > 4 )); then

				sed -i '/innodb_large_prefix/d' /etc/mysql/mariadb.conf.d/99-dietpi-4byte.cnf
				sed -i '/innodb_file_format/d' /etc/mysql/mariadb.conf.d/99-dietpi-4byte.cnf

			fi

			# Optimise for reduced memory use: https://github.com/MichaIng/DietPi/issues/605#issue-188930987
			#cat << _EOF_ > /etc/mysql/mariadb.conf.d/97-dietpi.cnf
#[mysqld]
#key_buffer_size=8M
#max_connections=30
#query_cache_size=8M
#query_cache_limit=512K
#thread_stack=128K
#_EOF_

		fi

		software_id=90 # phpMyAdmin
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Enable required PHP modules: https://docs.phpmyadmin.net/en/latest/require.html#php
			G_EXEC phpenmod ctype curl gd json mbstring xml zip

			# Install and enable webserver config
			# - Apache
			if (( ${aSOFTWARE_INSTALL_STATE[83]} > 0 )); then

				dps_index=$software_id Download_Install 'apache.phpmyadmin.conf' /etc/apache2/sites-available/dietpi-phpmyadmin.conf
				a2ensite dietpi-phpmyadmin

			# - Lighttpd
			elif (( ${aSOFTWARE_INSTALL_STATE[84]} > 0 )); then

				dps_index=$software_id Download_Install 'lighttpd.phpmyadmin.conf' /etc/lighttpd/conf-available/98-dietpi-phpmyadmin.conf
				G_EXEC_POST_FUNC(){ [[ $exit_code == 2 ]] && exit_code=0; } # Do not fail if modules are enabled already
				G_EXEC lighty-enable-mod dietpi-phpmyadmin

			# - Nginx
			elif (( ${aSOFTWARE_INSTALL_STATE[85]} > 0 )); then

				dps_index=$software_id Download_Install 'nginx.phpmyadmin.conf' /etc/apache2/sites-dietpi/dietpi-phpmyadmin.conf

			fi

			# Copy default config in place and adjust, if not already existent
			if [[ ! -f '/var/www/phpmyadmin/config.inc.php' ]]; then

				cp -a /var/www/phpmyadmin/config.sample.inc.php /var/www/phpmyadmin/config.inc.php
				GCI_PASSWORD=1 G_CONFIG_INJECT "\\\$cfg\\['blowfish_secret'][[:blank:]]*=" "\$cfg['blowfish_secret'] = '$(openssl rand -base64 32)';" /var/www/phpmyadmin/config.inc.php

			fi

			# Create MariaDB database and user
			if [[ -d '/mnt/dietpi_userdata/mysql/phpmyadmin' ]]; then

				G_DIETPI-NOTIFY 2 'phpMyAdmin MariaDB database found, will NOT overwrite.'

			else

				/boot/dietpi/func/create_mysql_db phpmyadmin phpmyadmin "$GLOBAL_PW"
				mysql phpmyadmin < /var/www/phpmyadmin/sql/create_tables.sql
				# Since "root" user cannot be used for login (unix_socket authentication), grant full admin privileges to "phpmyadmin"
				mysql -e 'grant all privileges on *.* to phpmyadmin@localhost with grant option'

			fi

			# Pre-create TempDir: https://docs.phpmyadmin.net/en/latest/config.html#cfg_TempDir
			mkdir /var/www/phpmyadmin/tmp
			chown www-data:www-data /var/www/phpmyadmin/tmp
			chmod 700 /var/www/phpmyadmin/tmp

		fi

		software_id=91 # Redis
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Enable redis php module, if installed
			phpenmod redis 2> /dev/null

			# Disable file logging and enable syslog instead, which resolves reported startup issues in cases as well: https://github.com/MichaIng/DietPi/issues/3291
			G_CONFIG_INJECT 'loglevel[[:blank:]]' 'loglevel warning' /etc/redis/redis.conf
			G_CONFIG_INJECT 'logfile[[:blank:]]' 'logfile ""' /etc/redis/redis.conf
			G_CONFIG_INJECT 'syslog-enabled[[:blank:]]' 'syslog-enabled yes' /etc/redis/redis.conf
			(( $G_DISTRO < 5 )) || G_CONFIG_INJECT 'always-show-logo[[:blank:]]' 'always-show-logo no' /etc/redis/redis.conf

		fi

		software_id=58 # OpenBazaar
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Data dir
			G_EXEC mkdir -p /mnt/dietpi_userdata/openbazaar

			# Init OB to have config file available, if not yet the case, and edit to allow remote client access
			if [[ ! -f '/mnt/dietpi_userdata/openbazaar/config' ]]; then

				openbazaar-go init -d /mnt/dietpi_userdata/openbazaar

				# Add current IP as gateway to allow external client connection
				G_CONFIG_INJECT '"Gateway": "' "    \"Gateway\": \"/ip4/$(mawk 'NR==4' /run/dietpi/.network)/tcp/4002\"," /mnt/dietpi_userdata/openbazaar/config

				# Client connection user/password
				G_DIETPI-NOTIFY 2 "Please enter username and password for your ${aSOFTWARE_NAME[$software_id]} client connection:"
				openbazaar-go setapicreds -d /mnt/dietpi_userdata/openbazaar

				# Client IP needs to be added to allowed IP list
				local ob_client_ip=
				local invalid_entry=
				while :
				do

					if G_WHIP_INPUTBOX "${invalid_entry}Please enter the IP address of your ${aSOFTWARE_NAME[$software_id]} client machine
This is required, since the ${aSOFTWARE_NAME[$software_id]} server node by default does not allow any remote connection." && [[ $G_WHIP_RETURNED_VALUE =~ ^[0-9.]+$ ]]; then

						ob_client_ip=$G_WHIP_RETURNED_VALUE
						break

					else

						invalid_entry='[FAILED] Please enter a valid IP address\n\n'

					fi

				done
				GCI_NEWLINE=1 G_CONFIG_INJECT '"AllowedIPs":' "    \"AllowedIPs\": [\n      \"$ob_client_ip\"\n    ]," /mnt/dietpi_userdata/openbazaar/config

			else

				G_DIETPI-NOTIFY 2 "Existing ${aSOFTWARE_NAME[$software_id]} config found. Skipping pre-configuration..."

			fi

			# User
			Create_User -d /mnt/dietpi_userdata/openbazaar openbazaar

			# Service
			cat << '_EOF_' > /etc/systemd/system/openbazaar.service
[Unit]
Description=OpenBazaar (DietPi)

[Service]
User=openbazaar
Environment=GOPATH=/mnt/dietpi_userdata/go
WorkingDirectory=/mnt/dietpi_userdata/openbazaar
ExecStart=/mnt/dietpi_userdata/go/bin/openbazaar-go start -d /mnt/dietpi_userdata/openbazaar -f -l notice

[Install]
WantedBy=multi-user.target
_EOF_
			# Permissions
			G_EXEC chown -R openbazaar:openbazaar /mnt/dietpi_userdata/openbazaar

		fi

		software_id=133 # YaCy
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Service
			cat << '_EOF_' > /etc/systemd/system/yacy.service
[Unit]
Description=YaCy (DietPi)

[Service]
Type=forking
ExecStart=/etc/yacy/startYACY.sh
ExecStop=/etc/yacy/stopYACY.sh

[Install]
WantedBy=multi-user.target
_EOF_

		fi

		software_id=2 # Folding@Home
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Remove obsolete config + data directories and SysV service + config
			[[ -d '/var/lib/fahclient' ]] && rm -R /var/lib/fahclient
			[[ -d '/etc/fahclient' ]] && rm -R /etc/fahclient
			[[ -f '/etc/default/fahclient' ]] && rm /etc/default/fahclient
			[[ -f '/etc/init.d/FAHClient' ]] && rm /etc/init.d/FAHClient
			update-rc.d -f FAHClient remove

			# Prepare our new config + data directory if not yet present
			if [[ ! -f '/mnt/dietpi_userdata/fahclient/config.xml' ]]; then

				G_EXEC mkdir -p /mnt/dietpi_userdata/fahclient
				dps_index=$software_id Download_Install 'config.xml' /mnt/dietpi_userdata/fahclient/config.xml

			fi

			# Service
			cat << _EOF_ > /etc/systemd/system/fahclient.service
[Unit]
Description=Folding@Home (DietPi)
Wants=network-online.target
After=network-online.target dietpi-boot.service

[Service]
User=fahclient
WorkingDirectory=/mnt/dietpi_userdata/fahclient
ExecStart=$(command -v FAHClient)

[Install]
WantedBy=multi-user.target
_EOF_
			# Permissions
			G_EXEC chown -R fahclient /mnt/dietpi_userdata/fahclient

		fi

		software_id=47 # ownCloud
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			G_DIETPI-NOTIFY 2 'Enabling needed PHP modules.' # https://doc.owncloud.org/server/administration_manual/installation/manual_installation.html#php-extensions
			G_EXEC phpenmod ctype curl dom gd iconv intl json mbstring pdo_mysql posix simplexml xmlreader xmlwriter zip fileinfo opcache apcu redis exif

			G_DIETPI-NOTIFY 2 'Enabling APCu memory cache for PHP command line usage (CLI) as well, including ownCloud occ command and cron jobs.'
			echo -e '; ownCloud PHP settings\n; priority=98\napc.enable_cli=1' > $FP_PHP_BASE_DIR/mods-available/dietpi-owncloud.ini
			G_EXEC phpenmod dietpi-owncloud

			# Get version string
			# shellcheck disable=SC2016
			local oc_version_major=$(sed -n '/$OC_VersionString/{s/^[^0-9]*//;s/\..*$//;p;q}' /var/www/owncloud/version.php)
			[[ $oc_version_major ]] || oc_version_major=10
			# shellcheck disable=SC2016
			local oc_version_minor=$(sed -n '/$OC_VersionString/{s/^[^.]*\.//;s/\..*$//;p;q}' /var/www/owncloud/version.php)
			[[ $oc_version_minor ]] || oc_version_minor=3

			if (( ${aSOFTWARE_INSTALL_STATE[83]} > 0 )); then

				G_DIETPI-NOTIFY 2 'Apache webserver found, enabling ownCloud specific configuration.' # https://doc.owncloud.org/server/administration_manual/installation/manual_installation.html#configure-apache-web-server
				a2enmod rewrite headers env dir mime 1> /dev/null
				local owncloud_conf='/etc/apache2/sites-available/dietpi-owncloud.conf'
				if [[ -f $owncloud_conf ]]; then

					owncloud_conf+='.dietpi-new'
					G_WHIP_MSG "Existing ownCloud Apache configuration found, will preserve the old one and save the new one for review and comparison to: $owncloud_conf"

				fi
				dps_index=$software_id Download_Install 'apache.owncloud.conf' $owncloud_conf
				a2ensite dietpi-owncloud 1> /dev/null
				# Cal/CardDAV redirects to ownCloud DAV endpoint
				if [[ ! -f '/etc/apache2/conf-available/dietpi-dav_redirect.conf' ]]; then

					echo '# Redirect Cal/CardDAV requests to ownCloud endpoint:
Redirect permanent /.well-known/carddav /owncloud/remote.php/dav
Redirect permanent /.well-known/caldav /owncloud/remote.php/dav' > /etc/apache2/conf-available/dietpi-dav_redirect.conf
					a2enconf dietpi-dav_redirect

				fi

			elif (( ${aSOFTWARE_INSTALL_STATE[84]} > 0 )); then

				G_DIETPI-NOTIFY 2 'Lighttpd webserver found, enabling ownCloud specific configuration.'

				# Enable required modules
				G_CONFIG_INJECT '"mod_access",' '	"mod_access",' /etc/lighttpd/lighttpd.conf '"mod_.+",'
				G_CONFIG_INJECT '"mod_setenv",' '	"mod_setenv",' /etc/lighttpd/lighttpd.conf '"mod_.+",'

				# Move ownCloud configuration file in place and activate it
				owncloud_conf='/etc/lighttpd/conf-available/99-dietpi-owncloud.conf'
				if [[ -f $owncloud_conf ]]; then

					owncloud_conf+='.dietpi-new'
					G_WHIP_MSG "Existing ownCloud Lighttpd configuration found, will preserve the old one and save the new one for review and comparison to: $nextcloud_conf"

				fi
				dps_index=$software_id Download_Install 'lighttpd.owncloud.conf' $owncloud_conf
				G_EXEC_POST_FUNC(){ [[ $exit_code == 2 ]] && exit_code=0; } # Do not fail if modules are enabled already
				G_EXEC lighty-enable-mod rewrite dietpi-owncloud

				# Cal/CardDAV redirects to ownCloud DAV endpoint
				if [[ ! -f '/etc/lighttpd/conf-enabled/99-dietpi-dav_redirect.conf' ]]; then

					echo '# Redirect Cal/CardDAV requests to ownCloud endpoint:
url.redirect += (
	"^/.well-known/caldav"  => "/owncloud/remote.php/dav",
	"^/.well-known/carddav" => "/owncloud/remote.php/dav"
)' > /etc/lighttpd/conf-available/99-dietpi-dav_redirect.conf
					G_EXEC_POST_FUNC(){ [[ $exit_code == 2 ]] && exit_code=0; } # Do not fail if modules are enabled already
					G_EXEC lighty-enable-mod dietpi-dav_redirect

				fi

			elif (( ${aSOFTWARE_INSTALL_STATE[85]} > 0 )); then

				G_DIETPI-NOTIFY 2 'Nginx webserver found, enabling ownCloud specific configuration.' # https://doc.owncloud.org/server/administration_manual/installation/nginx_configuration.html
				local owncloud_conf='/etc/nginx/sites-dietpi/dietpi-owncloud.conf'
				if [[ -f $owncloud_conf ]]; then

					owncloud_conf+='.dietpi-new'
					G_WHIP_MSG "Existing ownCloud Nginx configuration found, will preserve the old one and save the new one for review and comparison to: $owncloud_conf"

				fi
				dps_index=$software_id Download_Install 'nginx.owncloud.conf' $owncloud_conf

				# Uncomment HSTS header, if chosen via dietpi-letsencrypt and HTTPS connection test succeeds (incl. self-singed cert => exit code 5)
				if [[ -f '/boot/dietpi/.dietpi-letsencrypt' && $(mawk 'NR==4' /boot/dietpi/.dietpi-letsencrypt) == 1 ]]; then

					wget -q --spider --timeout=10 --tries=2 https://localhost &> /dev/null
					[[ $? == [05] ]] && sed -i 's/#add_header Strict-Transport-Security/add_header Strict-Transport-Security/g' $owncloud_conf

				fi

				# Disable pretty URLs (front controller) for ownCloud versions lower than 10:
				(( $oc_version_major < 10 )) && sed -i 's/^[[:blank:]]*fastcgi_param front_controller_active true;/\t\t#fastcgi_param front_controller_active true;/g' $owncloud_conf

				# Cal/CardDAV redirects to ownCloud DAV endpoint
				if [[ ! -f '/etc/nginx/sites-dietpi/dietpi-dav_redirect.conf' ]]; then

					# shellcheck disable=SC2016
					echo '# Redirect Cal/CardDAV requests to ownCloud endpoint:
location = /.well-known/carddav {
	return 301 $scheme://$host/owncloud/remote.php/dav;
}
location = /.well-known/caldav {
	return 301 $scheme://$host/owncloud/remote.php/dav;
}' > /etc/nginx/sites-dietpi/dietpi-dav_redirect.conf

				fi

			fi

			# Start MariaDB and Redis (required for reinstalls) for database creation and occ command
			G_EXEC systemctl restart mariadb
			G_EXEC systemctl restart redis-server

			# Initially add occ command shortcut, will be done by Dietpi-Globals automatically, if occ file exist:
			occ(){ sudo -u www-data php /var/www/owncloud/occ "$@"; }

			# Adjusting config file:
			local config_php='/var/www/owncloud/config/config.php'

			local datadir=$(grep -m1 '^[[:blank:]]*SOFTWARE_OWNCLOUD_DATADIR=' /boot/dietpi.txt | sed 's/^[^=]*=//')
			[[ $datadir ]] || datadir='/mnt/dietpi_userdata/owncloud_data'
			mkdir -p $datadir
			chown -R www-data:www-data $datadir

			if [[ -d '/mnt/dietpi_userdata/mysql/owncloud' ]]; then

				G_DIETPI-NOTIFY 2 'ownCloud database found, will NOT overwrite.'

			else

				if [[ -f $datadir/dietpi-owncloud-database-backup.sql ]]; then

					G_DIETPI-NOTIFY 2 'ownCloud database backup found, starting recovery...'
					local dbuser=$(grep -m1 "^[[:blank:]]*'dbuser'" $config_php | mawk '{print $3}' | sed "s/[',]//g")
					local dbpass=$(grep -m1 "^[[:blank:]]*'dbpassword'" $config_php | mawk '{print $3}' | sed "s/[',]//g")
					/boot/dietpi/func/create_mysql_db owncloud "$dbuser" "$dbpass"
					mysql owncloud < "$datadir"/dietpi-owncloud-database-backup.sql
					### Seems to be not needed anymore and can cause double entries: https://help.nextcloud.com/t/howto-change-move-data-directory-after-installation/17170/3?u=michaing
					# Adjust database data directory entry, in case it changed due to server migration:
					#mysql -e "update owncloud.oc_storages set id='local::$datadir/' where id rlike 'local::'"

				elif ! grep -q "'installed' => true," $config_php 2>/dev/null; then

					local username=$(grep -m1 '^[[:blank:]]*SOFTWARE_OWNCLOUD_NEXTCLOUD_USERNAME=' /boot/dietpi.txt | sed 's/^[^=]*=//')
					[[ $username ]] || username='admin'

					# For MariaDB, temporary database admin user needs to be created, as 'root' uses unix_socket login, which cannot be accessed by sudo -u www-data.
					mysql -e "grant all privileges on *.* to 'tmp_root'@'localhost' identified by '$GLOBAL_PW' with grant option"

					G_EXEC_DESC='ownCloud occ install'
					# - Replace password string internally to avoid printing it to console
					G_EXEC_PRE_FUNC(){ acommand[10]=$GLOBAL_PW acommand[14]=$GLOBAL_PW; }
					# - Checking output for stack trace to handle internal errors that do not lead to php error exit code
					G_EXEC_POST_FUNC(){ grep -qi 'Stack trace' $fp_log && exit_code=1337; }
					G_EXEC occ maintenance:install --no-interaction --database 'mysql' --database-name 'owncloud' --database-user 'tmp_root' --database-pass '<omitted>' --admin-user "$username" --admin-pass '<omitted>' --data-dir "$datadir"

					# Remove obsolete default data dir
					[[ $(readlink -f $datadir) != $(readlink -f /var/www/owncloud/data) ]] && rm -R /var/www/owncloud/data

					mysql -e 'drop user tmp_root@localhost'

				fi

			fi

			# Enable ownCloud to use 4-byte database
			G_CONFIG_INJECT "'mysql.utf8mb4'" "'mysql.utf8mb4' => true," $config_php "'dbpassword'"

			# Add local IP and hostname to trusted domains.
			# If "1 => '" does not exist, the config.php is not copied e.g. from older instance, so we add entries.
			if ! grep -q "1 => '" $config_php; then

				sed -i "/0 => 'localhost'/a     1 => '$(mawk 'NR==4' /run/dietpi/.network)'," $config_php
				sed -i "/1 => '/a     2 => '$(</etc/hostname)'," $config_php

			fi

			# Set CLI URL to ownCloud sub directory:
			sed -i "s|'http://localhost'|'http://localhost/owncloud'|g" $config_php

			# Set pretty URLs (without /index.php/) on Apache:
			if (( ${aSOFTWARE_INSTALL_STATE[83]} > 0 )); then

				GCI_PRESERVE=1 G_CONFIG_INJECT "'htaccess.RewriteBase'" "'htaccess.RewriteBase' => '/owncloud'," $config_php "'overwrite.cli.url'"
				occ maintenance:update:htaccess

			fi

			# APCu Memcache
			GCI_PRESERVE=1 G_CONFIG_INJECT "'memcache.local'" "'memcache.local' => '\\\\OC\\\\Memcache\\\\APCu'," $config_php "'version'"

			# Redis for transactional file locking:
			G_DIETPI-NOTIFY 2 'Enabling Redis for transactional file locking.' # https://doc.owncloud.org/server/administration_manual/configuration/server/caching_configuration.html#configuring-transactional-file-locking
			local redis_conf='/etc/redis/redis.conf'
			# - Enable Redis socket and grant www-data access to it
			GCI_PRESERVE=1 G_CONFIG_INJECT 'unixsocket[[:blank:]]' 'unixsocket /run/redis/redis-server.sock' $redis_conf
			G_CONFIG_INJECT 'unixsocketperm[[:blank:]]' 'unixsocketperm 770' $redis_conf
			G_EXEC usermod -aG redis www-data
			G_EXEC systemctl restart redis-server
			# - Enable ownCloud to use Redis socket for transactional file locking:
			G_CONFIG_INJECT "'filelocking.enabled'" "'filelocking.enabled' => true," $config_php "'memcache.local'"
			local redis_sock=$(grep -m1 '^[[:blank:]]*unixsocket[[:blank:]]' $redis_conf | mawk '{print $2}') # Re-estimate in case of existing custom path
			GCI_PRESERVE=1 GCI_NEWLINE=1 G_CONFIG_INJECT "'memcache.locking'" "'memcache.locking' => '\\\\OC\\\\Memcache\\\\Redis',\n'redis' => array ('host' => '$redis_sock', 'port' => 0,)," $config_php "'filelocking.enabled'"

			# Enable ownCloud background cron job:
			# - v10.3 comes with new occ system:cron command: https://doc.owncloud.org/server/10.3/admin_manual/release_notes.html#changes-to-background-job-execution
			local occ_command='occ system:cron'
			(( $oc_version_major < 10 || ( $oc_version_major == 10 && $oc_version_minor < 3 ) )) && occ_command='cron.php'
			crontab -u www-data -l 2> /dev/null | grep -q '/var/www/owncloud/.*cron' || ( crontab -u www-data -l 2> /dev/null ; echo "*/15 * * * * php /var/www/owncloud/$occ_command" ) | crontab -u www-data -
			occ background:cron

			# Enable maintenance mode to allow handling by dietpi-services:
			grep -q "^[[:blank:]]*'maintenance' => true," $config_php || occ maintenance:mode --on

			# On <1 GiB devices assure at least 512 MiB swap space are available to stand 512 MiB file uploads + increased PHP cache and session file usage: https://github.com/MichaIng/DietPi/issues/2293
			(( $RAM_PHYS < 924 && $(free -m | mawk '/^Swap:/{print $2;exit}') < 512 )) && /boot/dietpi/func/dietpi-set_swapfile 512

		fi

		software_id=114 # Nextcloud
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			G_DIETPI-NOTIFY 2 'Enabling required PHP modules.' # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation
			G_EXEC phpenmod ctype curl dom gd iconv intl json mbstring pdo_mysql posix simplexml xmlreader xmlwriter zip fileinfo opcache apcu redis exif

			G_DIETPI-NOTIFY 2 'Apply PHP override settings for Nextcloud.' # https://docs.nextcloud.com/server/stable/admin_manual/configuration_server/server_tuning.html#enable-php-opcache
			echo -e '; Nextcloud PHP settings\n; priority=98\napc.enable_cli=1\nopcache.enable=1\nopcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000\nopcache.memory_consumption=128\nopcache.save_comments=1\nopcache.revalidate_freq=1\nmemory_limit=512M' > $FP_PHP_BASE_DIR/mods-available/dietpi-nextcloud.ini
			G_EXEC phpenmod dietpi-nextcloud

			if (( ${aSOFTWARE_INSTALL_STATE[83]} > 0 )); then

				G_DIETPI-NOTIFY 2 'Apache webserver found, enabling Nextcloud specific configuration.' # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#apache-web-server-configuration
				a2enmod rewrite headers env dir mime 1> /dev/null
				local nextcloud_conf='/etc/apache2/sites-available/dietpi-nextcloud.conf'
				if [[ -f $nextcloud_conf ]]; then

					nextcloud_conf+='.dietpi-new'
					G_WHIP_MSG "Existing Nextcloud Apache configuration found, will preserve the old one and save the new one for review and comparison to: $nextcloud_conf"

				fi
				dps_index=$software_id Download_Install 'apache.nextcloud.conf' $nextcloud_conf
				a2ensite dietpi-nextcloud 1> /dev/null
				# Cal/CardDAV redirects to Nextcloud DAV endpoint
				if [[ ! -f '/etc/apache2/conf-available/dietpi-dav_redirect.conf' ]]; then

					echo '# Redirect Cal/CardDAV requests to Nextcloud endpoint:
Redirect permanent /.well-known/carddav /nextcloud/remote.php/dav
Redirect permanent /.well-known/caldav /nextcloud/remote.php/dav' > /etc/apache2/conf-available/dietpi-dav_redirect.conf
					a2enconf dietpi-dav_redirect

				fi

			elif (( ${aSOFTWARE_INSTALL_STATE[84]} > 0 )); then

				G_DIETPI-NOTIFY 2 'Lighttpd webserver found, enabling Nextcloud specific configuration.'

				# Enable required modules
				G_CONFIG_INJECT '"mod_access",' '	"mod_access",' /etc/lighttpd/lighttpd.conf '"mod_.+",'
				G_CONFIG_INJECT '"mod_setenv",' '	"mod_setenv",' /etc/lighttpd/lighttpd.conf '"mod_.+",'

				# Move Nextcloud configuration file in place and activate it
				nextcloud_conf='/etc/lighttpd/conf-available/99-dietpi-nextcloud.conf'
				if [[ -f $nextcloud_conf ]]; then

					nextcloud_conf+='.dietpi-new'
					G_WHIP_MSG "Existing Nextcloud Lighttpd configuration found, will preserve the old one and save the new one for review and comparison to: $nextcloud_conf"

				fi
				dps_index=$software_id Download_Install 'lighttpd.nextcloud.conf' $nextcloud_conf
				G_EXEC_POST_FUNC(){ [[ $exit_code == 2 ]] && exit_code=0; } # Do not fail if modules are enabled already
				G_EXEC lighty-enable-mod rewrite dietpi-nextcloud

				# Cal/CardDAV redirects to Nextcloud DAV endpoint
				if [[ ! -f '/etc/lighttpd/conf-enabled/99-dietpi-dav_redirect.conf' ]]; then

					echo '# Redirect Cal/CardDAV requests to Nextcloud endpoint:
url.redirect += (
	"^/.well-known/caldav"  => "/nextcloud/remote.php/dav",
	"^/.well-known/carddav" => "/nextcloud/remote.php/dav"
)' > /etc/lighttpd/conf-available/99-dietpi-dav_redirect.conf
					G_EXEC_POST_FUNC(){ [[ $exit_code == 2 ]] && exit_code=0; } # Do not fail if modules are enabled already
					G_EXEC lighty-enable-mod dietpi-dav_redirect

				fi

			elif (( ${aSOFTWARE_INSTALL_STATE[85]} > 0 )); then

				G_DIETPI-NOTIFY 2 'Nginx webserver found, enabling Nextcloud specific configuration.' # https://docs.nextcloud.com/server/stable/admin_manual/installation/nginx.html
				local nextcloud_conf='/etc/nginx/sites-dietpi/dietpi-nextcloud.conf'
				if [[ -f $nextcloud_conf ]]; then

					nextcloud_conf+='.dietpi-new'
					G_WHIP_MSG "Existing Nextcloud Nginx configuration found, will preserve the old one and save the new one for review and comparison to: $nextcloud_conf"

				fi
				dps_index=$software_id Download_Install 'nginx.nextcloud.conf' $nextcloud_conf

				# Uncomment HSTS header, if chosen via dietpi-letsencrypt and HTTPS connection test succeeds (incl. self-singed cert => exit code 5)
				if [[ -f '/boot/dietpi/.dietpi-letsencrypt' && $(mawk 'NR==4' /boot/dietpi/.dietpi-letsencrypt) == 1 ]]; then

					wget -q --spider --timeout=10 --tries=2 https://localhost &> /dev/null
					[[ $? == [05] ]] && sed -i 's/#add_header Strict-Transport-Security/add_header Strict-Transport-Security/g' $nextcloud_conf

				fi

				# Disable pretty URLs (front controller), if Nextcloud version is lower than 13
				# shellcheck disable=SC2016
				if (( $(sed -n '/$OC_VersionString/{s/^[^0-9]*//;s/\..*$//;p;q}' /var/www/nextcloud/version.php) < 13 )); then

					sed -i 's/^[[:blank:]]*fastcgi_param front_controller_active true;/\t\t#fastcgi_param front_controller_active true;/g' $nextcloud_conf

				fi

				# Cal/CardDAV redirects to Nextcloud DAV endpoint
				if [[ ! -f '/etc/nginx/sites-dietpi/dietpi-dav_redirect.conf' ]]; then

					# shellcheck disable=SC2016
					echo '# Redirect Cal/CardDAV requests to Nextcloud endpoint:
location = /.well-known/carddav {
	return 301 $scheme://$host/nextcloud/remote.php/dav;
}
location = /.well-known/caldav {
	return 301 $scheme://$host/nextcloud/remote.php/dav;
}' > /etc/nginx/sites-dietpi/dietpi-dav_redirect.conf

				fi

			fi

			# Start MariaDB and Redis (required for reinstalls) for database creation and ncc command
			G_EXEC systemctl restart mariadb
			G_EXEC systemctl restart redis-server

			# Initially add occ command shortcut, will be done by Dietpi-Globals automatically, if occ file exist:
			ncc(){ sudo -u www-data php /var/www/nextcloud/occ "$@"; }

			# Adjusting config file:
			local config_php='/var/www/nextcloud/config/config.php'

			local datadir=$(grep -m1 '^[[:blank:]]*SOFTWARE_NEXTCLOUD_DATADIR=' /boot/dietpi.txt | sed 's/^[^=]*=//')
			[[ $datadir ]] || datadir='/mnt/dietpi_userdata/nextcloud_data'
			mkdir -p $datadir
			chown www-data:www-data $datadir

			if [[ -d '/mnt/dietpi_userdata/mysql/nextcloud' ]]; then

				G_DIETPI-NOTIFY 2 'Nextcloud database found, will NOT overwrite.'
				if [[ ! -f $config_php ]]; then

					G_WHIP_MSG '[WARNING] Existing Nextcloud database was found, but no related install directory\n
A remaining MariaDB "nextcloud" database from an earlier installed instance was found. But the related install directory "/var/www/nextcloud/config/config.php" does not exist.
Since running a fresh install with an existing database can produce data corruption, if the versions do not exactly match, you either need to remove the database or find and place the related install directory.\n
We cannot predict your aim and do not want to mess or break your data, so please do this manually.\n
To remove the existing database (including e.g. contacts, calendar, file tags etc.):
	# mysqladmin drop nextcloud
Otherwise to copy an existing instance in place:
	# rm -R /var/www/nextcloud
	# cp -a /path/to/existing/nextcloud/. /var/www/nextcloud/
	# chown -R www-data:www-data /var/www/nextcloud\n
The install script will now exit. After applying one of the the above, rerun dietpi-software, e.g.:
	# dietpi-software install 114'

					[[ -f /var/www/nextcloud/occ ]] && rm /var/www/nextcloud/occ
					/boot/dietpi/dietpi-services start
					exit 1

				fi

			else

				if [[ -f $datadir/dietpi-nextcloud-database-backup.sql ]]; then

					G_DIETPI-NOTIFY 2 'Nextcloud database backup found, starting recovery...'
					local dbuser=$(grep -m1 "^[[:blank:]]*'dbuser'" $config_php | mawk '{print $3}' | sed "s/[',]//g")
					local dbpass=$(grep -m1 "^[[:blank:]]*'dbpassword'" $config_php | mawk '{print $3}' | sed "s/[',]//g")
					/boot/dietpi/func/create_mysql_db nextcloud "$dbuser" "$dbpass"
					mysql nextcloud < $datadir/dietpi-nextcloud-database-backup.sql
					### Seems to be not needed anymore and can cause double entries: https://help.nextcloud.com/t/howto-change-move-data-directory-after-installation/17170/3?u=michaing
					# Adjust database data directory entry, in case it changed due to server migration:
					#mysql -e "update nextcloud.oc_storages set id='local::$datadir/' where id rlike 'local::'"

				elif ! grep -q "'installed' => true," $config_php 2>/dev/null; then

					local username=$(grep -m1 '^[[:blank:]]*SOFTWARE_OWNCLOUD_NEXTCLOUD_USERNAME=' /boot/dietpi.txt | sed 's/^[^=]*=//')
					[[ $username ]] || username='admin'

					# For MariaDB, temporary database admin user needs to be created, as 'root' uses unix_socket login, which cannot be accessed by sudo -u www-data.
					mysql -e "grant all privileges on *.* to 'tmp_root'@'localhost' identified by '$GLOBAL_PW' with grant option"

					G_EXEC_DESC='Nextcloud ncc install'
					# - Replace password string internally to avoid printing it to console
					G_EXEC_PRE_FUNC(){ acommand[10]=$GLOBAL_PW acommand[14]=$GLOBAL_PW; }
					# - Checking output for stack trace to handle internal errors that do not lead to php error exit code
					# - Workaround Nextcloud 14.0.3 throwing an error, when data dir path contains a symlink: https://github.com/nextcloud/server/issues/12247
					G_EXEC_POST_FUNC(){

						if (( $exit_code )); then

							grep -q 'Following symlinks is not allowed' $fp_log && { cp -a /var/www/nextcloud/core/skeleton/. "$datadir/$username/files/"; exit_code=0; }

						else

							grep -qi 'Stack trace' $fp_log && exit_code=1337

						fi

					}
					G_EXEC ncc maintenance:install --no-interaction --database 'mysql' --database-name 'nextcloud' --database-user 'tmp_root' --database-pass '<omitted>' --admin-user "$username" --admin-pass '<omitted>' --data-dir "$datadir"

					# Remove obsolete default data dir
					[[ $(readlink -f $datadir) != $(readlink -f /var/www/nextcloud/data) ]] && rm -R /var/www/nextcloud/data

					mysql -e 'drop user tmp_root@localhost'

				fi

			fi

			# Enable Nextcloud to use 4-byte database
			G_CONFIG_INJECT "'mysql.utf8mb4'" "'mysql.utf8mb4' => true," $config_php "'dbpassword'"

			# Disable trusted_domains.
			grep -q "1 => '*'" $config_php || sed -i "/0 => 'localhost'/a     1 => '*'," $config_php

			# Set CLI URL to Nextcloud sub directory:
			sed -i "s|'http://localhost'|'http://localhost/nextcloud'|g" $config_php

			# Set pretty URLs (without /index.php/) on Apache:
			if (( ${aSOFTWARE_INSTALL_STATE[83]} > 0 )); then

				GCI_PRESERVE=1 G_CONFIG_INJECT "'htaccess.RewriteBase'" "'htaccess.RewriteBase' => '/nextcloud'," $config_php "'overwrite.cli.url'"
				ncc maintenance:update:htaccess

			fi

			# APCu Memcache
			GCI_PRESERVE=1 G_CONFIG_INJECT "'memcache.local'" "'memcache.local' => '\\\\OC\\\\Memcache\\\\APCu'," $config_php "'version'"

			# Redis for transactional file locking:
			G_DIETPI-NOTIFY 2 'Enabling Redis for transactional file locking.' # https://docs.nextcloud.com/server/stable/admin_manual/configuration_files/files_locking_transactional.html
			local redis_conf="/etc/redis/redis.conf"
			# - Enable Redis socket and grant www-data access to it
			GCI_PRESERVE=1 G_CONFIG_INJECT 'unixsocket[[:blank:]]' 'unixsocket /run/redis/redis-server.sock' $redis_conf
			G_CONFIG_INJECT 'unixsocketperm[[:blank:]]' 'unixsocketperm 770' $redis_conf
			G_EXEC usermod -aG redis www-data
			G_EXEC systemctl restart redis-server
			# - Enable Nextloud to use Redis socket:
			G_CONFIG_INJECT "'filelocking.enabled'" "'filelocking.enabled' => true," $config_php "'memcache.local'"
			local redis_sock=$(grep -m1 '^[[:blank:]]*unixsocket[[:blank:]]' $redis_conf | mawk '{print $2}') # Re-estimate in case of existing custom path
			GCI_PRESERVE=1 GCI_NEWLINE=1 G_CONFIG_INJECT "'memcache.locking'" "'memcache.locking' => '\\\\OC\\\\Memcache\\\\Redis',\n'redis' => array ('host' => '$redis_sock', 'port' => 0,)," $config_php "'filelocking.enabled'"

			# Tweak Argon2 hashing
			# - Use all available CPU threads
			GCI_PRESERVE=1 G_CONFIG_INJECT "'hashingThreads'" "'hashingThreads' => ${G_HW_CPU_CORES}," $config_php "'version'"
			# - ToDo: Configure the other settings after getting some clarifiations: https://github.com/nextcloud/server/pull/19023#issuecomment-660071524
			#GCI_PRESERVE=1 G_CONFIG_INJECT "'hashingMemoryCost'" "'hashingMemoryCost' => 65536," $config_php "'hashingThreads'"
			#GCI_PRESERVE=1 G_CONFIG_INJECT "'hashingTimeCost'" "'hashingTimeCost' => 4," $config_php "'hashingMemoryCost'"

			# Enable Nextcloud background cron job: https://docs.nextcloud.com/server/17/admin_manual/configuration_server/background_jobs_configuration.html#cron
			crontab -u www-data -l 2> /dev/null | grep -q '/var/www/nextcloud/cron.php' || ( crontab -u www-data -l 2> /dev/null ; echo '*/5 * * * * php /var/www/nextcloud/cron.php' ) | crontab -u www-data -
			ncc background:cron

			# Convert filecache table to bigint, which is not done automatically by Nextcloud since v15
			ncc db:convert-filecache-bigint -n

			# Add missing database columns and indices, which is not done automatically by Nextcloud
			ncc db:add-missing-columns
			ncc db:add-missing-indices

			# Enable maintenance mode to allow handling by dietpi-services:
			grep -q "^[[:blank:]]*'maintenance' => true," $config_php || ncc maintenance:mode --on

			# On <1 GiB devices assure at least 512 MiB swap space are available to stand 512 MiB file uploads + increased PHP cache and session file usage: https://github.com/MichaIng/DietPi/issues/2293
			(( $RAM_PHYS < 924 && $(free -m | mawk '/^Swap:/{print $2;exit}') < 512 )) && /boot/dietpi/func/dietpi-set_swapfile 512

		fi

		software_id=168 # Nextcloud Talk
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			G_DIETPI-NOTIFY 2 'Configuring TURN server:'
			# Buster: systemd unit
			if [[ -f '/lib/systemd/system/coturn.service' ]]; then

				# Remove sysvinit service traces
				[[ -f '/etc/init.d/coturn' ]] && rm /etc/init.d/coturn
				update-rc.d -f coturn remove
				[[ -f '/etc/default/coturn' ]] && rm /etc/default/coturn

				# Disable coturn logging by default, this can be overridden via /etc/turnserver.conf
				G_EXEC mkdir -p /etc/systemd/system/coturn.service.d
				echo -e "[Service]\nExecStart=\n$(grep -m1 "^[[:blank:]]*ExecStart=" /lib/systemd/system/coturn.service) -l stdout --no-stdout-log --simple-log" > /etc/systemd/system/coturn.service.d/dietpi-logging.conf

			# Stretch: sysvinit service
			else

				# Enable init.d service
				G_CONFIG_INJECT 'TURNSERVER_ENABLED=' 'TURNSERVER_ENABLED=1' /etc/default/coturn
				# Disable coturn logging by default, this can be overridden via /etc/turnserver.conf
				G_CONFIG_INJECT 'DAEMON_ARGS=' "DAEMON_ARGS='-c /etc/turnserver.conf -o -l stdout --no-stdout-log --simple-log'" /etc/default/coturn

			fi

			# Ask user for server domain and desired TURN server port
			local invalid_text=
			local domain=$(hostname -f)
			while :
			do

				G_WHIP_DEFAULT_ITEM=$domain
				if G_WHIP_INPUTBOX "${invalid_text}Please enter your servers external domain to allow Nextcloud Talk access your TURN server:" && [[ $G_WHIP_RETURNED_VALUE ]]; then

					domain=${G_WHIP_RETURNED_VALUE#http*://}
					break

				else

					invalid_text='[ERROR] No valid entry found. Please retry...\n\n'

				fi

			done
			invalid_text=
			local port=3478
			while :
			do

				G_WHIP_DEFAULT_ITEM=$port
				if G_WHIP_INPUTBOX "${invalid_text}Please enter the network port, that should be used for your TURN server:\n
NB: This port needs to be forwarded by your router and/or opened in your firewall settings. Default value is: 3478" && disable_error=1 G_CHECK_VALIDINT "$G_WHIP_RETURNED_VALUE" 0; then

					port=$G_WHIP_RETURNED_VALUE
					break

				else

					invalid_text='[ERROR] No valid entry found, value needs to be a sequence of integers. Please retry...\n\n'

				fi

			done

			# Adjust coTURN settings
			# - If /etc/turnserver.conf is not present, use default or create empty file
			if [[ ! -f '/etc/turnserver.conf' ]]; then

				# shellcheck disable=SC2015
				[[ -f '/usr/share/doc/coturn/examples/etc/turnserver.conf.gz' ]] && gzip -cd /usr/share/doc/coturn/examples/etc/turnserver.conf.gz > /etc/turnserver.conf || > /etc/turnserver.conf

			fi
			# - Estimate coTURN version to handle outdated settings correctly
			local version=$(dpkg-query -Wf '${Version}' coturn); version=${version:0:7}; version=${version//./}
			# https://help.nextcloud.com/t/howto-setup-nextcloud-talk-with-turn-server/30794
			G_CONFIG_INJECT 'listening-port=' "listening-port=$port" /etc/turnserver.conf
			G_CONFIG_INJECT 'fingerprint' 'fingerprint' /etc/turnserver.conf
			G_CONFIG_INJECT 'use-auth-secret' 'use-auth-secret' /etc/turnserver.conf
			if (( $version < 4508 )); then

				G_CONFIG_INJECT 'lt-cred-mech' 'lt-cred-mech' /etc/turnserver.conf

			else

				sed -i 's/^[[:blank:]]*lt-cred-mech/#lt-cred-mech/' /etc/turnserver.conf

			fi
			G_CONFIG_INJECT 'realm=' "realm=$domain" /etc/turnserver.conf
			GCI_PRESERVE=1 G_CONFIG_INJECT 'total-quota=' 'total-quota=100' /etc/turnserver.conf
			GCI_PRESERVE=1 G_CONFIG_INJECT 'bps-capacity=' 'bps-capacity=0' /etc/turnserver.conf
			G_CONFIG_INJECT 'stale-nonce' 'stale-nonce' /etc/turnserver.conf
			if (( $version < 4510 )); then

				G_CONFIG_INJECT 'no-loopback-peers' 'no-loopback-peers' /etc/turnserver.conf

			else

				sed -i 's/^[[:blank:]]*allow-loopback-peers/#allow-loopback-peers/' /etc/turnserver.conf
				sed -i '/^[[:blank:]]*no-loopback-peers/d' /etc/turnserver.conf

			fi
			G_CONFIG_INJECT 'no-multicast-peers' 'no-multicast-peers' /etc/turnserver.conf

			# Install Nextcloud Talk app
			G_EXEC systemctl start mariadb
			G_EXEC systemctl start redis-server
			G_EXEC ncc maintenance:mode --off
			if [[ ! -d '/var/www/nextcloud/apps/spreed' ]]; then

				# Succeed if app is already installed and on "Cannot declare class" bug: https://github.com/MichaIng/DietPi/issues/3499#issuecomment-622955490
				G_EXEC_POST_FUNC(){ [[ $exit_code != 0 && $(<$fp_log) =~ (' already installed'$|' Cannot declare class ') ]] && exit_code=0; }
				G_EXEC ncc app:install spreed

			fi
			ncc app:enable spreed

			# Adjust Nextcloud Talk settings to use coturn
			ncc config:app:set spreed stun_servers --value="[\"$domain:$port\"]"
			# - Generate random secret to secure TURN server access
			local secret=$(openssl rand -hex 32)
			GCI_PASSWORD=1 GCI_PRESERVE=1 G_CONFIG_INJECT 'static-auth-secret=' "static-auth-secret=$secret" /etc/turnserver.conf
			# - Scrape existing secret, in case user manually chose/edited it
			secret=$(sed -n '/^[[:blank:]]*static-auth-secret=/{s/^[^=]*=//p;q}' /etc/turnserver.conf)
			ncc config:app:set spreed turn_servers --value="[{\"server\":\"$domain:$port\",\"secret\":\"$secret\",\"protocols\":\"udp,tcp\"}]" | sed 's/"secret":".*","protocols"/"secret":"<OMITTED>","protocols"/'
			unset secret domain port invalid_text version

		fi

		software_id=44 # Transmission
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Remove obsolete service and environment files
			[[ -f '/etc/init.d/transmission-daemon' ]] && rm -v /etc/init.d/transmission-daemon
			update-rc.d -f transmission-daemon remove
			[[ -f '/etc/default/transmission-daemon' ]] && rm -v /etc/default/transmission-daemon
			[[ -f '/etc/init/transmission-daemon.conf' ]] && rm -v /etc/init/transmission-daemon.conf

			# Make "dietpi" the primary group and "debian-transmission" a supplementary group, to make downloads R/W accessible for other media software and network shares.
			G_EXEC usermod -g dietpi -aG debian-transmission debian-transmission

			# Apply optimised settings
			G_CONFIG_INJECT '"cache-size-mb"' '    "cache-size-mb": '"$(Optimise_BitTorrent 0)"',' /etc/transmission-daemon/settings.json '^\{$'
			G_CONFIG_INJECT '"download-queue-size"' '    "download-queue-size": '"$(Optimise_BitTorrent 1)"',' /etc/transmission-daemon/settings.json '^\{$'
			G_CONFIG_INJECT '"peer-limit-global"' '    "peer-limit-global": '"$(Optimise_BitTorrent 2)"',' /etc/transmission-daemon/settings.json '^\{$'
			G_CONFIG_INJECT '"upload-slots-per-torrent"' '    "upload-slots-per-torrent": '"$(Optimise_BitTorrent 3)"',' /etc/transmission-daemon/settings.json '^\{$'

			G_CONFIG_INJECT '"port-forwarding-enabled"' '    "port-forwarding-enabled": true,' /etc/transmission-daemon/settings.json '^\{$'
			G_CONFIG_INJECT '"encryption"' '    "encryption": 2,' /etc/transmission-daemon/settings.json '^\{$'

			G_CONFIG_INJECT '"idle-seeding-limit"' '    "idle-seeding-limit": 1,' /etc/transmission-daemon/settings.json '^\{$'
			G_CONFIG_INJECT '"idle-seeding-limit-enabled"' '    "idle-seeding-limit-enabled": true,' /etc/transmission-daemon/settings.json '^\{$'
			G_CONFIG_INJECT '"ratio-limit"' '    "ratio-limit": 1.1,' /etc/transmission-daemon/settings.json '^\{$'
			G_CONFIG_INJECT '"ratio-limit-enabled"' '    "ratio-limit-enabled": true,' /etc/transmission-daemon/settings.json '^\{$'
			G_CONFIG_INJECT '"trash-original-torrent-files"' '    "trash-original-torrent-files": true,' /etc/transmission-daemon/settings.json '^\{$'

			G_CONFIG_INJECT '"download-dir"' '    "download-dir": "/mnt/dietpi_userdata/downloads",' /etc/transmission-daemon/settings.json '^\{$'
			# - Double quotes need to be escaped in .json files: https://github.com/MichaIng/DietPi/issues/2484#issuecomment-481374924
			# - ToDo: Hash password directly. Which hash method is used?
			GCI_PASSWORD=1 G_CONFIG_INJECT '"rpc-password"' '    "rpc-password": "'"${GLOBAL_PW//\"/\\\"}"'",' /etc/transmission-daemon/settings.json '^\{$'
			G_CONFIG_INJECT '"rpc-username"' '    "rpc-username": "root",' /etc/transmission-daemon/settings.json '^\{$'
			G_CONFIG_INJECT '"rpc-whitelist-enabled"' '    "rpc-whitelist-enabled": false,' /etc/transmission-daemon/settings.json '^\{$'
			G_CONFIG_INJECT '"message-level"' '    "message-level": 0,' /etc/transmission-daemon/settings.json '^\{$'

			# To allow access for download managers and media software, files need to be created with 77X/66X permissions.
			G_CONFIG_INJECT '"umask"' '    "umask": 7,' /etc/transmission-daemon/settings.json '^\{$'

		fi

		software_id=54 # phpBB
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Preserve old database name
			[[ -d '/mnt/dietpi_userdata/mysql/phpbb3' ]] || /boot/dietpi/func/create_mysql_db phpbb phpbb "$GLOBAL_PW"

			# Permissions: The web-based installer writes setup and database access information to this file. Upload, cache and store directories are shipped with 777 modes already.
			G_EXEC chown www-data /var/www/phpbb/config.php

		fi

		software_id=128 # MPD
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# User
			Create_User -g audio -G dietpi -d /mnt/dietpi_userdata/.mpd_cache mpd

			# Dirs
			# - Purge old log and cache
			G_EXEC rm -Rf /var/log/mpd /mnt/dietpi_userdata/.mpd_cache
			# - Recreate log and cache dirs
			G_EXEC mkdir -p /var/log/mpd /mnt/dietpi_userdata/.mpd_cache
			# - Remove obsolete cache dir, preserve previous music and playlists, in case of an existing custom install
			[[ ! -e '/var/lib/mpd/music' || -L '/var/lib/mpd/music' ]] || mv -n /var/lib/mpd/music/* /mnt/dietpi_userdata/Music/ &> /dev/null
			[[ ! -e '/var/lib/mpd/playlists' || -L '/var/lib/mpd/playlists' ]] || mv -n /var/lib/mpd/playlists/* /mnt/dietpi_userdata/Music/ &> /dev/null
			[[ -d '/var/lib/mpd' ]] && G_EXEC rm -R /var/lib/mpd

			# Config
			G_BACKUP_FP /etc/mpd.conf
			dps_index=$software_id Download_Install 'mpd.conf' /etc/mpd.conf

			# Service
			# - Remove obsolete sysvinit service and config
			[[ -f '/etc/init.d/mpd' ]] && rm /etc/init.d/mpd
			update-rc.d -f mpd remove
			[[ -f '/etc/default/mpd' ]] && rm /etc/default/mpd
			# - Upstream systemd unit: https://github.com/MusicPlayerDaemon/MPD/blob/master/systemd/system/mpd.service.in
			# - Debian package systemd unit: https://deb.debian.org/debian/pool/main/m/mpd/
			# - Run systemd unit as mpd user instead of using config-file based user change, to preserve supplementary group permissions (i.e. dietpi)
			# - Define config file explicitly, since custom /usr/local/bin/mpd binary searches for /usr/local/etc/mpd.conf by default
			cat << _EOF_ > /lib/systemd/system/mpd.service
[Unit]
Description=Music Player Daemon (DietPi)
Documentation=man:mpd(1) man:mpd.conf(5)
Wants=network-online.target
After=network-online.target sound.target dietpi-boot.service

[Service]
Type=notify
User=mpd
PermissionsStartOnly=true
ExecStartPre=$(command -v mkdir) -p /run/mpd
ExecStartPre=$(command -v chown) -R mpd: /run/mpd
ExecStart=$(command -v mpd) --no-daemon /etc/mpd.conf

# allow MPD to use real-time priority 50
LimitRTPRIO=50
LimitRTTIME=infinity

# disallow writing to /usr, /bin, /sbin, ...
ProtectSystem=yes

# more paranoid security settings
NoNewPrivileges=yes
ProtectKernelTunables=yes
ProtectControlGroups=yes
ProtectKernelModules=yes
# AF_NETLINK is required by libsmbclient, or it will exit() .. *sigh*
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX AF_NETLINK
RestrictNamespaces=yes

[Install]
WantedBy=multi-user.target
Also=mpd.socket
_EOF_
			# - systemd socket activation, not shipped by our custom package
			[[ -f '/lib/systemd/system/mpd.socket' ]] || cat << _EOF_ > /lib/systemd/system/mpd.socket
[Socket]
ListenStream=/run/mpd/socket
ListenStream=6600
Backlog=5
KeepAlive=true
PassCredentials=true

[Install]
WantedBy=sockets.target
_EOF_

			# JustBoom specials
			if grep -q '^[[:blank:]]*CONFIG_SOUNDCARD=justboom' /boot/dietpi.txt; then

				# Name displayed in ympd sound button
				local justboom_soundcard_desc='JustBoom DietPi'
				G_CONFIG_INJECT 'name "' "name \"$justboom_soundcard_desc\"" /etc/mpd.conf
				G_CONFIG_INJECT 'zeroconf_name "' "zeroconf_name \"$justboom_soundcard_desc\"" /etc/mpd.conf

				# Output (192khz 32bit)
				local target_bitdepth=32
				local target_rate=192000
				G_CONFIG_INJECT 'format "' "format \"$target_rate:$target_bitdepth:2\"" /etc/mpd.conf
				G_CONFIG_INJECT 'audio_output_format "' "audio_output_format \"$target_rate:$target_bitdepth:2\"" /etc/mpd.conf

				# Set SOXR quality
				# - All RPi's can handle SOXR VH @ 192khz 32bit: https://github.com/MichaIng/DietPi/issues/581#issuecomment-256643079
				G_CONFIG_INJECT 'samplerate_converter "' 'samplerate_converter "soxr very high"' /etc/mpd.conf # highest

			fi

			# Grab our test music for the user
			Download_Test_Media

			# Permissions
			chown -R mpd: /var/log/mpd /mnt/dietpi_userdata/.mpd_cache /etc/mpd.conf

		fi

		software_id=94 # ProFTPD
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Config
			G_BACKUP_FP /etc/proftpd/proftpd.conf
			dps_index=$software_id Download_Install 'conf' /etc/proftpd/proftpd.conf

			# Do not allow root access via FTP
			sed -i 's/^[[:blank:]]*root/#root/' /etc/ftpusers

		fi

		software_id=96 # Samba Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			echo -e "$GLOBAL_PW\n$GLOBAL_PW" | smbpasswd -s -a dietpi

			local fp_conf='/etc/samba/smb.conf'
			G_BACKUP_FP $fp_conf
			dps_index=$software_id Download_Install 'conf' $fp_conf

			G_CONFIG_INJECT 'max connections =' "max connections = $(( $G_HW_CPU_CORES * 2 ))" $fp_conf

		fi

		software_id=95 # vsftpd
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Config
			G_BACKUP_FP /etc/vsftpd.conf
			dps_index=$software_id Download_Install 'conf' /etc/vsftpd.conf

			# Do not allow root access via FTP
			sed -i 's/^[[:blank:]]*root/#root/' /etc/ftpusers

		fi

		software_id=109 # NFS Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration
			mkdir -p /etc/exports.d
			[[ -f '/etc/exports.d/dietpi.exports' ]] || echo '/mnt/dietpi_userdata *(rw,async,no_root_squash,fsid=0,crossmnt,no_subtree_check)' > /etc/exports.d/dietpi.exports

		fi

		software_id=184 # Tor Relay
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			local nickname=
			G_WHIP_BUTTON_CANCEL_TEXT='Skip'
			G_WHIP_INPUTBOX 'Pick a nickname for your relay.\n\nNB: This is optional, select "Skip" to leave it empty.' && nickname=$G_WHIP_RETURNED_VALUE

			local email=
			G_WHIP_BUTTON_CANCEL_TEXT='Skip'
			G_WHIP_INPUTBOX 'Pick a contact email for your relay.\n\nNB: This is optional but encouraged, and it will be published depending on your relay.' && email=$G_WHIP_RETURNED_VALUE

			local orport=443
			local invalid_text=
			while :
			do
				G_WHIP_DEFAULT_ITEM=$orport
				if G_WHIP_INPUTBOX "${invalid_text}Please enter the ORPort that people will use to connect to your Tor relay:
\nNB: This port needs to be forwarded by your router and/or opened in your firewall settings. Default value is: 443, which should be changed when running a webserver.
\nDo not use 9001, as this port is commonly associated with Tor and censors might scan for it." && disable_error=1 G_CHECK_VALIDINT "$G_WHIP_RETURNED_VALUE" 1
				then
					orport=$G_WHIP_RETURNED_VALUE
					break
				else
					invalid_text='[ERROR] No valid entry found, value needs to be a sequence of integers. Please retry...\n\n'
				fi
			done

			local ipv6=
			if grep -q '^[[:blank:]]*CONFIG_ENABLE_IPV6=1' /boot/dietpi.txt
			then
				for i in $(hostname -I)
				do
					[[ $i == *.* ]] && continue
					G_WHIP_DEFAULT_ITEM=$i
					break
				done
				G_WHIP_BUTTON_CANCEL_TEXT='Skip'
				G_WHIP_INPUTBOX "Please enter the private IPv6 address of this device. Skip this if you only want to use IPv4, or if this device doesn't have an IPv6 address." && ipv6=$G_WHIP_RETURNED_VALUE
			fi

			local choice_required=
			while :
			do
				G_WHIP_MENU_ARRAY=(
					'Bridge' ': Use this machine as an obfs4 bridge. This is the safest to run from home.'
					'Guard/Middle' ': Use this machine as a beginning or middle relay. Some info will be made public.'
					'Exit' ': Use this machine as an exit relay. This is most at risk for legal complaints.'
				)

				G_WHIP_MENU "${choice_required}Please choose which type of relay this should be set up as:" || { choice_required='[ERROR] A choice is required to finish the Tor install.\n\n'; continue; }

				if [[ $G_WHIP_RETURNED_VALUE == 'Exit' ]]
				then
					G_WHIP_BUTTON_OK_TEXT='YES'
					G_WHIP_BUTTON_CANCEL_TEXT='NO'
					G_WHIP_YESNO 'WARNING: DO NOT RUN AN EXIT RELAY FROM HOME.
\nAlso, it is highly recommended that you setup exit relays on dedicated servers.
\nBefore running an exit relay, check with your hosting provider to make sure they allow it.
\nAlso, set a reverse DNS (PTR) record (tor-exit is a good name), and preferably edit your WHOIS record to show that this is a Tor exit node.
\nWould you still like to continue?' || continue
				fi

				break
			done

			if [[ $G_WHIP_RETURNED_VALUE == 'Bridge' ]]
			then
				G_AGI obfs4proxy
				local obfs4port=80
				invalid_text=
				while :
				do
					G_WHIP_DEFAULT_ITEM=$obfs4port
					if G_WHIP_INPUTBOX "${invalid_text}Please enter the obfs4 port that people will use to connect to your bridge:
\nNB: This port needs to be forwarded by your router and/or opened in your firewall settings. Default value is: 80. Do not use default value if running a webserver.
\nDo not use 9001, as this port is commonly associated with Tor and censors might scan for it." && disable_error=1 G_CHECK_VALIDINT "$G_WHIP_RETURNED_VALUE" 1; then
						obfs4port=$G_WHIP_RETURNED_VALUE
						break
					else
						invalid_text='[ERROR] No valid entry found, value needs to be a sequence of integers. Please retry...\n\n'
					fi
				done

				# Grant capability to bind to privileged ports below 1024
				(( $obfs4port < 1024 )) && G_EXEC setcap cap_net_bind_service=+ep "$(command -v obfs4proxy)"

				cat << _EOF_ > /etc/tor/torrc
BridgeRelay 1
ORPort $orport
ServerTransportPlugin obfs4 exec $(command -v obfs4proxy)
ServerTransportListenAddr obfs4 0.0.0.0:$obfs4port
ExtORPort auto
_EOF_
				G_EXEC mkdir -p /etc/systemd/system/tor@.service.d
				G_EXEC eval "echo -e '[Service]\nNoNewPrivileges=no' > /etc/systemd/system/tor@.service.d/dietpi-relay.conf"
				G_EXEC systemctl daemon-reload

			elif [[ $G_WHIP_RETURNED_VALUE == 'Guard/Middle' ]]
			then

				cat << _EOF_ > /etc/tor/torrc
ORPort $orport
ExitRelay 0
SocksPort 0
ControlSocket 0
_EOF_
			elif [[ $G_WHIP_RETURNED_VALUE == 'Exit' ]]
			then
				G_AGI unbound
				aSOFTWARE_INSTALL_STATE[182]=1

				cat << _EOF_ > /etc/tor/torrc
ORPort $orport
ExitRelay 1
SocksPort 0
ControlSocket 0

ExitPolicy accept *:20-21
ExitPolicy accept *:43
ExitPolicy accept *:53
ExitPolicy accept *:79
ExitPolicy accept *:80-81
ExitPolicy accept *:88
ExitPolicy accept *:110
ExitPolicy accept *:143
ExitPolicy accept *:220
ExitPolicy accept *:389
ExitPolicy accept *:443
ExitPolicy accept *:464
ExitPolicy accept *:531
ExitPolicy accept *:543-544
ExitPolicy accept *:554
ExitPolicy accept *:636
ExitPolicy accept *:706
ExitPolicy accept *:749
ExitPolicy accept *:873
ExitPolicy accept *:902-904
ExitPolicy accept *:981
ExitPolicy accept *:989-990
ExitPolicy accept *:991
ExitPolicy accept *:992
ExitPolicy accept *:993
ExitPolicy accept *:995
ExitPolicy accept *:1194
ExitPolicy accept *:1220
ExitPolicy accept *:1293
ExitPolicy accept *:1500
ExitPolicy accept *:1533
ExitPolicy accept *:1677
ExitPolicy accept *:1723
ExitPolicy accept *:1755
ExitPolicy accept *:1863
ExitPolicy accept *:2082
ExitPolicy accept *:2083
ExitPolicy accept *:2086-2087
ExitPolicy accept *:2095-2096
ExitPolicy accept *:2102-2104
ExitPolicy accept *:3690
ExitPolicy accept *:4321
ExitPolicy accept *:4643
ExitPolicy accept *:5050
ExitPolicy accept *:5190
ExitPolicy accept *:5222-5223
ExitPolicy accept *:5228
ExitPolicy accept *:8008
ExitPolicy accept *:8074
ExitPolicy accept *:8082
ExitPolicy accept *:8087-8088
ExitPolicy accept *:8232-8233
ExitPolicy accept *:8332-8333
ExitPolicy accept *:8443
ExitPolicy accept *:8888
ExitPolicy accept *:9418
ExitPolicy accept *:10000
ExitPolicy accept *:11371
ExitPolicy accept *:19294
ExitPolicy accept *:19638
ExitPolicy accept *:50002
ExitPolicy accept *:64738
ExitPolicy reject *:*
_EOF_
				[[ $ipv6 ]] && echo 'IPv6Exit 1' >> /etc/tor/torrc
				# Apply Unbound as local DNS resolver for privacy reasons
				command -v resolvconf > /dev/null && G_EXEC sed -i 's/dns-nameservers.*$/dns-nameservers 127.0.0.1/' /etc/network/interfaces
				G_EXEC eval "echo 'nameserver 127.0.0.1' > /etc/resolv.conf" # Unbound is running and configured in the very next config step, so it is safe to switch the DNS nameserver right now.
			fi

			echo 'ControlPort 9051' >> /etc/tor/torrc
			echo 'CookieAuthentication 1' >> /etc/tor/torrc
			[[ $nickname ]] && echo "Nickname $nickname" >> /etc/tor/torrc
			[[ $email ]] && echo "ContactInfo $email" >> /etc/tor/torrc
			[[ $ipv6 ]] && echo "ORPort [$ipv6]:$orport" >> /etc/tor/torrc

		fi

		software_id=32 # ympd
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# User
			Create_User -g dietpi ympd

			# Service
			cat << '_EOF_' > /etc/systemd/system/ympd.service
[Unit]
Description=ympd (DietPi)
After=mpd.service

[Service]
ExecStart=/usr/bin/ympd --user ympd --webport 1337

[Install]
WantedBy=multi-user.target
_EOF_
		fi

		software_id=148 # myMPD
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Switch to primary group "dietpi" to allow media access and create media files with "dietpi" group: https://github.com/MichaIng/DietPi/issues/3382
			G_EXEC usermod -g dietpi mympd
			getent group mympd > /dev/null && G_EXEC_NOFAIL=1 G_EXEC delgroup mympd

			# Adjust config file on fresh install
			# - On reinstall /etc/mympd/mympd.conf.dist will be created, so use its existence as reinstall flag
			if [[ ! -f '/etc/mympd.conf.dist' ]]; then

				G_CONFIG_INJECT 'host[[:blank:]]*=' 'host = /run/mpd/socket' /etc/mympd.conf '^\[mpd\]'
				G_CONFIG_INJECT 'playlistdirectory[[:blank:]]*=' 'playlistdirectory = /mnt/dietpi_userdata/Music' /etc/mympd.conf '^\[mpd\]'
				G_CONFIG_INJECT 'webport[[:blank:]]*=' 'webport = 1333' /etc/mympd.conf '^\[webserver\]'
				G_CONFIG_INJECT 'ssl[[:blank:]]*=' 'ssl = false' /etc/mympd.conf '^\[webserver\]'

			fi

		fi

		software_id=121 # Roon Bridge
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# User: !!! root required for remote updates
			#Create_User -g dietpi -G audio -d /mnt/dietpi_userdata/roon roon

			dps_index=$software_id Download_Install 'roonbridge.service' /etc/systemd/system/roonbridge.service

			# Move log files to /var/log (dietpi-ramlog)
			G_EXEC mkdir -p /var/log/roon /mnt/dietpi_userdata/roon/{RoonBridge,RAATServer}
			# - Clear symlink locations
			G_EXEC rm -Rf /mnt/dietpi_userdata/roon/{RoonBridge,RAATServer}/Logs
			G_EXEC ln -s /var/log/roon /mnt/dietpi_userdata/roon/RoonBridge/Logs
			G_EXEC ln -s /var/log/roon /mnt/dietpi_userdata/roon/RAATServer/Logs

			# Permissions
			#chown -R roon:dietpi /mnt/dietpi_userdata/roon /var/log/roon

		fi

		software_id=125 # Tomcat8
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			local version=11
			(( $G_DISTRO < 5 || G_HW_MODEL == 1 )) && version=8
			G_CONFIG_INJECT 'JAVA_HOME=' "JAVA_HOME=$(find /usr/lib/jvm/ -name "java-$version-openjdk*")" /etc/default/tomcat8

		fi

		software_id=119 # CAVA
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# CAVA conf, preserve existing
			mkdir -p /root/.config/cava
			[[ -f '/root/.config/cava/config' ]] || dps_index=$software_id Download_Install 'cava.conf' /root/.config/cava/config

			# Lower MPD buffer size to reduce latency of spectrum:
			sed -i '/audio_buffer_size /c\audio_buffer_size "384"' /etc/mpd.conf

			# fifo stream for mpd
			if ! grep -q '/tmp/mpd.fifo' /etc/mpd.conf; then

				cat << '_EOF_' >> /etc/mpd.conf

# Cava fifo stream
audio_output {

    type "fifo"
    enabled "yes"
    name "CAVA"
    path "/tmp/mpd.fifo"
    format "44100:16:2"

}
_EOF_
			fi

		fi

		software_id=118 # Mopidy
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Assure user home, data and cache dir as well on custom configs
			G_CONFIG_INJECT 'data_dir[[:blank:]]*=' 'data_dir = /mnt/dietpi_userdata/mopidy/data' /etc/mopidy/mopidy.conf '\[core\]'
			G_CONFIG_INJECT 'cache_dir[[:blank:]]*=' 'cache_dir = /mnt/dietpi_userdata/mopidy/cache' /etc/mopidy/mopidy.conf '\[core\]'

			# Move existing home+data to dietpi_userdata, if not yet existent
			if [[ -d '/var/lib/mopidy' && ! -d '/mnt/dietpi_userdata/mopidy' ]]; then

				G_EXEC mv /var/lib/mopidy /mnt/dietpi_userdata/mopidy
				# Workaround error about moving data dir into itself
				G_EXEC mkdir -p /mnt/dietpi_userdata/mopidy/.hidden_data
				# Non-hidden files/folders are data
				G_EXEC mv /mnt/dietpi_userdata/mopidy/* /mnt/dietpi_userdata/mopidy/.hidden_data
				G_EXEC mv /mnt/dietpi_userdata/mopidy/.hidden_data /mnt/dietpi_userdata/mopidy/data

			else

				G_EXEC mkdir -p /mnt/dietpi_userdata/mopidy/data
				[[ -d '/var/lib/mopidy' ]] && rm -R /var/lib/mopidy

			fi

			# Move existing cache to dietpi_userdata, if not yet existent
			if [[ -d '/var/cache/mopidy' && ! -d '/mnt/dietpi_userdata/mopidy/cache' ]]; then

				G_EXEC mv /var/cache/mopidy /mnt/dietpi_userdata/mopidy/cache

			else

				G_EXEC mkdir -p /mnt/dietpi_userdata/mopidy/cache
				[[ -d '/var/cache/mopidy' ]] && rm -R /var/cache/mopidy

			fi

			# Permissions
			G_EXEC chown -R mopidy:root /mnt/dietpi_userdata/mopidy

			# User
			G_EXEC usermod -g dietpi -aG audio -d /mnt/dietpi_userdata/mopidy mopidy

			# Do not pre-create obsolete cache dir
			G_EXEC mkdir -p /etc/systemd/system/mopidy.service.d
			echo -e '[Service]\nExecStartPre=' > /etc/systemd/system/mopidy.service.d/dietpi.conf

			Download_Test_Media

		fi

		software_id=31 # Kodi
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Remove Kodi user (Whilst waving)
			getent passwd kodi > /dev/null && userdel kodi

			# Run Kodi as root
			[[ -f '/etc/default/kodi' ]] && G_CONFIG_INJECT 'USER=' 'USER=root' /etc/default/kodi

			# Desktop entry
			G_THREAD_START curl -sSfL "https://raw.githubusercontent.com/$G_GITOWNER/DietPi/$G_GITBRANCH/.conf/desktop/icons/kodi-icon.png" -o /var/lib/dietpi/dietpi-software/installed/desktop/icons/kodi-icon.png
			G_THREAD_START curl -sSfL "https://raw.githubusercontent.com/$G_GITOWNER/DietPi/$G_GITBRANCH/.conf/desktop/apps/kodi.desktop" -o /usr/share/applications/kodi.desktop

			# Copy udev rules, probably not needed for root, but we'll do it anyway
			dps_index=$software_id Download_Install '99-dietpi-kodi.rules' /etc/udev/rules.d/99-dietpi-kodi.rules

			# Desktop shortcut
			Create_Desktop_Shortcut kodi

			G_THREAD_WAIT

		fi

		software_id=39 # MiniDLNA
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# User: Make "dietpi" the primary group to enable cross-access to media files
			Create_User -g dietpi -G minidlna -d /var/lib/minidlna minidlna

			# Remove obsolete service files
			[[ -f '/etc/init.d/minidlna' ]] && G_EXEC rm /etc/init.d/minidlna
			G_EXEC update-rc.d -f minidlna remove
			[[ -f '/lib/systemd/system/minidlna.service' ]] && G_EXEC rm /lib/systemd/system/minidlna.service
			[[ -f '/etc/default/minidlna' ]] && G_EXEC rm /etc/default/minidlna
			[[ -d '/var/log/minidlna' ]] && G_EXEC rm -R /var/log/minidlna

			# Service
			cat << _EOF_ > /etc/systemd/system/minidlna.service
[Unit]
Description=MiniDLNA (DietPi)
Wants=network-online.target
After=network-online.target dietpi-boot.service

[Service]
User=minidlna
RuntimeDirectory=minidlna
ExecStart=$(command -v minidlnad) -S -R -f /etc/minidlna.conf

[Install]
WantedBy=multi-user.target
_EOF_
			# Config
			G_BACKUP_FP /etc/minidlna.conf
			dps_index=$software_id Download_Install 'minidlna.conf' /etc/minidlna.conf

			# Cache
			G_EXEC mkdir -p /mnt/dietpi_userdata/.MiniDLNA_Cache
			G_EXEC chown -R minidlna:dietpi /mnt/dietpi_userdata/.MiniDLNA_Cache

			Download_Test_Media

		fi

		software_id=67 # No-IP
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Failsafe: Directory required for "noip2 -C" to create the config file
			G_EXEC mkdir -p /usr/local/etc

			# Service
			cat << _EOF_ > /etc/systemd/system/noip2.service
[Unit]
Description=No-IP update client (DietPi)
Wants=network-online.target
After=network-online.target dietpi-boot.service
ConditionPathExists=/usr/local/etc/no-ip2.conf

[Service]
Type=forking
ExecStart=/usr/local/bin/noip2
ExecStop=$(command -v killall) -w noip2

[Install]
WantedBy=multi-user.target
_EOF_
		fi

		software_id=108 # Amiberry
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Reinstall: Remove obsolete config file: https://github.com/midwan/amiberry/releases/tag/v2.25
			[[ -f '/mnt/dietpi_userdata/amiberry/conf/adfdir.conf' ]] && G_EXEC rm /mnt/dietpi_userdata/amiberry/conf/adfdir.conf

			# Service
			cat << '_EOF_' > /etc/systemd/system/amiberry.service
[Unit]
Description=Amiberry Amiga Emulator (DietPi)
Documentation=https://github.com/midwan/amiberry/wiki

[Service]
WorkingDirectory=/mnt/dietpi_userdata/amiberry
Environment=LD_LIBRARY_PATH=/mnt/dietpi_userdata/amiberry/lib
ExecStart=/mnt/dietpi_userdata/amiberry/amiberry

[Install]
WantedBy=local-fs.target
_EOF_
			# Permissions: Enable access for kickstart uploads via file servers
			G_EXEC chown :dietpi /mnt/dietpi_userdata/amiberry/kickstarts
			G_EXEC chmod 0775 /mnt/dietpi_userdata/amiberry/kickstarts

		fi

		software_id=112 # DXX-Rebirth
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Symlink savegames to root
			# - Remove existing syslinks
			G_EXEC rm -Rf /root/.d{1,2}x-rebirth

			G_EXEC ln -sf /mnt/dietpi_userdata/dxx-rebirth/descent_1_profiles /root/.d1x-rebirth
			G_EXEC ln -sf /mnt/dietpi_userdata/dxx-rebirth/descent_2_profiles /root/.d2x-rebirth

			# +exe
			G_EXEC chmod -R +x /mnt/dietpi_userdata/dxx-rebirth/*

			# Desktop menu entry
			G_EXEC mkdir -p /usr/share/applications
			G_EXEC ln -sf /mnt/dietpi_userdata/dxx-rebirth/dxx-rebirth.desktop /usr/share/applications/dxx-rebirth.desktop

			# Desktop shortcut
			Create_Desktop_Shortcut dxx-rebirth

		fi

		software_id=51 # OpenTyrian
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Desktop menu entry
			G_THREAD_START curl -sSfL "https://raw.githubusercontent.com/$G_GITOWNER/DietPi/$G_GITBRANCH/.conf/desktop/apps/opentyrian.desktop" -o /usr/share/applications/opentyrian.desktop

			# Copy the DietPi run file for OpenTyrian
			dps_index=$software_id Download_Install 'run' /usr/games/opentyrian/run
			G_EXEC chmod +x /usr/games/opentyrian/run

			# Desktop shortcut
			Create_Desktop_Shortcut opentyrian

			G_THREAD_WAIT

		fi

		software_id=59 # RPi Cam Control
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# raspimjpeg conf
			G_EXEC ln -sf /etc/raspimjpeg /var/www/rpicam/raspimjpeg

			# Motion conf
			G_EXEC chown motion:www-data /etc/motion/motion.conf
			G_EXEC chmod 664 /etc/motion/motion.conf
			G_EXEC usermod -aG video www-data

			# raspimjpeg/php schedule startup and control script
			dps_index=$software_id Download_Install 'raspimjpeg.sh' /var/lib/dietpi/dietpi-software/installed/raspimjpeg.sh
			G_EXEC chmod +x /var/lib/dietpi/dietpi-software/installed/raspimjpeg.sh
			cat << '_EOF_' > /etc/systemd/system/raspimjpeg.service
[Unit]
Description=Raspimjpeg (DietPi)

[Service]
RemainAfterExit=yes
ExecStart=/var/lib/dietpi/dietpi-software/installed/raspimjpeg.sh start
ExecStop=/var/lib/dietpi/dietpi-software/installed/raspimjpeg.sh stop

[Install]
WantedBy=multi-user.target
_EOF_
			# Replace confs with /var/www to /var/www/rpicam, once
			if ! grep -q '/rpicam' /etc/raspimjpeg; then

				G_EXEC sed -i 's#/var/www#/var/www/rpicam#g' /etc/raspimjpeg
				G_EXEC sed -i 's#/var/www#/var/www/rpicam#g' /etc/motion/motion.conf

			fi

			# Setup Data directory
			G_EXEC mkdir -p /mnt/dietpi_userdata/rpicam
			G_EXEC rm -Rf /var/www/rpicam/media
			G_EXEC ln -s /mnt/dietpi_userdata/rpicam /var/www/rpicam/media

			# Enable RPi camera
			/boot/dietpi/func/dietpi-set_hardware rpi-camera enable

			# Permissions
			G_EXEC chown -R www-data:www-data /var/www/rpicam /mnt/dietpi_userdata/rpicam

		fi

		software_id=45 # Deluge
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# User
			Create_User -G dietpi -d /mnt/dietpi_userdata/deluge debian-deluged

			# Dirs
			G_EXEC mkdir -p /mnt/dietpi_userdata/deluge/.config/deluge /var/log/deluged

			# Service: https://git.deluge-torrent.org/deluge/tree/packaging/systemd/deluged.service
			cat << _EOF_ > /etc/systemd/system/deluged.service
[Unit]
Description=Deluge Daemon (DietPi)
Documentation=man:deluged

[Service]
User=debian-deluged
Group=dietpi
UMask=002
ExecStart=$(command -v deluged) -d -l /var/log/deluged/daemon.log -L warning
Restart=on-failure
TimeoutStopSec=300

[Install]
WantedBy=multi-user.target
_EOF_
			# https://git.deluge-torrent.org/deluge/tree/packaging/systemd/deluge-web.service
			cat << _EOF_ > /etc/systemd/system/deluge-web.service
[Unit]
Description=Deluge Web UI (DietPi)
Documentation=man:deluge-web

[Service]
User=debian-deluged
UMask=027
ExecStart=$(command -v deluge-web) -l /var/log/deluged/web.log -L warning
Restart=on-failure

[Install]
WantedBy=multi-user.target
_EOF_
			# Add web.log to logrotate config
			if [[ -f '/etc/logrotate.d/deluged' ]] && ! grep -q 'web.log' /etc/logrotate.d/deluged; then

				echo -e "\n$(</etc/logrotate.d/deluged)" >> /etc/logrotate.d/deluged
				sed -i '1,1s/daemon.log/web.log/' /etc/logrotate.d/deluged

			fi

			# Adjust config files only, if not yet existent
			if [[ ! -f '/mnt/dietpi_userdata/deluge/.config/deluge/core.conf' ]]; then

				# Copy DietPi configs
				dps_index=$software_id Download_Install 'deluge.conf' /mnt/dietpi_userdata/deluge/.config/deluge/core.conf
				dps_index=$software_id Download_Install 'deluge_web.conf' /mnt/dietpi_userdata/deluge/.config/deluge/web.conf

				# Set remote access login details
				echo "root:$GLOBAL_PW:10" > /mnt/dietpi_userdata/deluge/.config/deluge/auth

				# Apply optimised settings
				# - Cache size is in steps of 16 KiB. (Cache size * 16 = total KiB)
				sed -i '/"cache_size": /c\ "cache_size": '$(( $(Optimise_BitTorrent 0) * 1024 / 16 ))',' /mnt/dietpi_userdata/deluge/.config/deluge/core.conf
				sed -i '/"max_active_limit": /c\ "max_active_limit": '"$(Optimise_BitTorrent 1)"',' /mnt/dietpi_userdata/deluge/.config/deluge/core.conf
				sed -i '/"max_active_downloading": /c\ "max_active_downloading": '"$(Optimise_BitTorrent 1)"',' /mnt/dietpi_userdata/deluge/.config/deluge/core.conf
				sed -i '/"max_connections_global": /c\ "max_connections_global": '"$(Optimise_BitTorrent 2)"',' /mnt/dietpi_userdata/deluge/.config/deluge/core.conf
				sed -i '/"max_upload_slots_global": /c\ "max_upload_slots_global": '"$(Optimise_BitTorrent 3)"',' /mnt/dietpi_userdata/deluge/.config/deluge/core.conf

			fi

			# Remove sysvinit service leftovers, installed by Debian APT package
			[[ -f '/etc/init.d/deluged' ]] && rm -v /etc/init.d/deluged
			update-rc.d -f deluged remove
			[[ -f '/etc/default/deluged' ]] && rm -v /etc/default/deluged
			[[ -d '/var/lib/deluged' ]] && rm -R /var/lib/deluged

			# Permissions
			chown -R debian-deluged:debian-deluged /mnt/dietpi_userdata/deluge
			chown -R debian-deluged /var/log/deluged

		fi

		software_id=115 # Webmin
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Remove obsolete SysV service
			[[ -f '/etc/init.d/webmin' ]] && rm /etc/init.d/webmin
			update-rc.d -f webmin remove

			# Service
			cat << '_EOF_' > /etc/systemd/system/webmin.service
[Unit]
Description=Webmin (DietPi)

[Service]
Type=forking
ExecStart=/etc/webmin/start
ExecStop=/etc/webmin/stop

[Install]
WantedBy=multi-user.target
_EOF_
		fi

		software_id=93 # Pi-hole
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Enable required PHP modules: https://github.com/pi-hole/pi-hole/blob/development/automated%20install/basic-install.sh#L250
			G_EXEC phpenmod xml sqlite3 intl

			# Webserver configs
			# - Lighttpd
			if (( ${aSOFTWARE_INSTALL_STATE[84]} > 0 )); then

				# Enable required modules
				G_CONFIG_INJECT '"mod_setenv",' '	"mod_setenv",' /etc/lighttpd/lighttpd.conf '"mod_.+",'

				# Add and enable Pi-hole config
				dps_index=$software_id Download_Install 'lighttpd.pihole.conf' /etc/lighttpd/conf-available/99-dietpi-pihole.conf
				G_EXEC_POST_FUNC(){ [[ $exit_code == 2 ]] && exit_code=0; } # Do not fail if modules are enabled already
				G_EXEC lighty-enable-mod dietpi-pihole

				# Optional config to block public access to admin panel
				dps_index=$software_id Download_Install 'lighttpd.block_public_admin.conf' /etc/lighttpd/conf-available/99-dietpi-pihole-block_public_admin.conf
				local enable_cmd='lighty-enable-mod dietpi-pihole-block_public_admin'
				local disable_cmd='lighty-disable-mod dietpi-pihole-block_public_admin'

			# - Apache
			elif (( ${aSOFTWARE_INSTALL_STATE[83]} > 0 )); then

				# Enable required modules
				G_EXEC a2enmod headers

				# Add and enable Pi-hole config
				dps_index=$software_id Download_Install 'apache.pihole.conf' /etc/apache2/sites-available/dietpi-pihole.conf
				G_EXEC a2ensite dietpi-pihole

				# Optional config to block public access to admin panel
				dps_index=$software_id Download_Install 'apache.block_public_admin.conf' /etc/apache2/sites-available/dietpi-pihole-block_public_admin.conf
				local enable_cmd='a2ensite dietpi-pihole-block_public_admin'
				local disable_cmd='a2dissite dietpi-pihole-block_public_admin'

			# - Nginx
			elif (( ${aSOFTWARE_INSTALL_STATE[85]} > 0 )); then

				# Add Pi-hole config
				dps_index=$software_id Download_Install 'nginx.pihole.conf' /etc/nginx/sites-dietpi/dietpi-pihole.conf

				# Optional config to block public access to admin panel
				dps_index=$software_id Download_Install 'nginx.block_public_admin.conf' /etc/nginx/sites-dietpi/dietpi-pihole-block_public_admin.off
				local enable_cmd='mv /etc/nginx/sites-dietpi/dietpi-pihole-block_public_admin.{off,on}'
				local disable_cmd='mv /etc/nginx/sites-dietpi/dietpi-pihole-block_public_admin.{on,off}'

			fi

			# Ask user if they want to block public access to the admin panel
			G_WHIP_BUTTON_OK_TEXT='YES'
			G_WHIP_BUTTON_CANCEL_TEXT='NO'
			G_WHIP_YESNO "In order to increase security, it is recommended that you block public access to the Pi-hole admin panel, so that unless you're on a local network, or connected via VPN, the Pi-hole admin panel isn't accessible.\n
You can always enable/disable this at a later point using the commands:
 - $enable_cmd
 - $disable_cmd\n
Do you want to block public access to the admin panel now?" && eval "$enable_cmd"

			# Create symlinks to align Pi-hole admin and 404 page relative to webroot
			G_EXEC cd /var/www
			# - Move existing files/dirs out of the way
			[[ ! -L 'admin' && -e 'admin' ]] && G_EXEC mv admin admin.bak
			G_EXEC ln -sf html/admin admin
			[[ ! -L 'pihole' && -e 'pihole' ]] && G_EXEC mv pihole pihole.bak
			G_EXEC ln -sf html/pihole pihole

			# Pi-hole v5: Add webserver user to pihole group to allow gravity.db access from web UI: https://github.com/MichaIng/DietPi/issues/3391
			G_EXEC usermod -aG pihole www-data

			# Apply most resource friendly and officially recommended NULL blocking: https://docs.pi-hole.net/ftldns/blockingmode/
			G_CONFIG_INJECT 'BLOCKINGMODE=' 'BLOCKINGMODE=NULL' /etc/pihole/pihole-FTL.conf

			# Failsafe: Reset permissions
			G_EXEC cd /var/www/html/admin
			G_EXEC git reset --hard HEAD
			G_EXEC cd /tmp/$G_PROGRAM_NAME

			# Disable non-required dhcpcd, which is installed by Pi-hole installer
			systemctl disable --now dhcpcd

			# Disable pihole.log query logging: https://github.com/pi-hole/FTL/issues/614#issuecomment-510564476
			G_EXEC pihole -l off

			# Set web interface PW: https://github.com/MichaIng/DietPi/issues/662
			pihole -a -p "$GLOBAL_PW"

		fi

		software_id=33 # Airsonic
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# User
			Create_User -g dietpi -G audio -d /mnt/dietpi_userdata/airsonic airsonic

			# Optimise memory limit
			local memory_limit=$(( $RAM_PHYS / 5 ))
			(( $memory_limit < 200 )) && memory_limit=200

			# Service
			cat << _EOF_ > /etc/systemd/system/airsonic.service
[Unit]
Description=Airsonic Media Server (DietPi)
Wants=network-online.target
After=network-online.target remote-fs.target dietpi-boot.service

[Service]
User=airsonic
WorkingDirectory=/mnt/dietpi_userdata/airsonic
ExecStart=$(command -v java) -Xmx${memory_limit}m -Dairsonic.home=/mnt/dietpi_userdata/airsonic -Dserver.context-path=/airsonic -Dserver.port=8080 -jar /mnt/dietpi_userdata/airsonic/airsonic.war

[Install]
WantedBy=multi-user.target
_EOF_
			# Enable FFmpeg transcode
			G_EXEC mkdir -p /mnt/dietpi_userdata/airsonic/transcode
			command -v ffmpeg > /dev/null && G_EXEC ln -sf "$(command -v ffmpeg)" /mnt/dietpi_userdata/airsonic/transcode

			# Permissions
			G_EXEC chmod +x /mnt/dietpi_userdata/airsonic/airsonic.war
			G_EXEC chown airsonic:root /mnt/dietpi_userdata/airsonic

			# Grab our test media for user
			Download_Test_Media

		fi

		software_id=34 # Subsonic
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# User
			Create_User -G dietpi,audio -d /var/subsonic subsonic

			# Config
			# - Optimise memory limit
			local memory_limit=$(( $RAM_PHYS / 5 ))
			(( $memory_limit < 200 )) && memory_limit=200
			G_CONFIG_INJECT 'SUBSONIC_USER=' 'SUBSONIC_USER=subsonic' /etc/default/subsonic
			G_CONFIG_INJECT 'SUBSONIC_ARGS=' "SUBSONIC_ARGS='--quiet --pidfile=/run/subsonic.pid --max-memory=$memory_limit --default-music-folder=/mnt/dietpi_userdata/Music --default-podcast-folder=/mnt/dietpi_userdata/Music --default-playlist-folder=/mnt/dietpi_userdata/Music'" /etc/default/subsonic

			# Enable FFmpeg transcode
			G_EXEC mkdir -p /var/subsonic/transcode
			command -v ffmpeg > /dev/null && G_EXEC ln -sf "$(command -v ffmpeg)" /var/subsonic/transcode

			# Grab our test media for user
			Download_Test_Media

		fi

		software_id=71 # WebIOPi
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Autostart
			update-rc.d webiopi defaults

		fi

		software_id=98 # HAProxy
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Jail directory
			G_EXEC mkdir -p /var/lib/haproxy

			# Config
			[[ -f '/etc/haproxy/haproxy.cfg' ]] || cat << '_EOF_' > /etc/haproxy/haproxy.cfg
global

	maxconn 64
	# Jail directory
	chroot /var/lib/haproxy
	stats socket /run/haproxy.sock mode 660 level admin
	stats timeout 30s
	user root
	group root
	daemon

	# Default SSL material locations
	ca-base /etc/ssl/certs
	crt-base /etc/ssl/private

	# Default ciphers to use on SSL-enabled listening sockets.
	# For more information, see ciphers(1SSL).
	ssl-default-bind-ciphers kEECDH+aRSA+AES:kRSA+AES:+AES256:RC4-SHA:!kEDH:!LOW:!EXP:!MD5:!aNULL:!eNULL

defaults

	log     global
	mode    http
	option  httplog
	option  dontlognull
	timeout connect 5000
	timeout client  50000
	timeout server  50000
	errorfile 400 /etc/haproxy/errors/400.http
	errorfile 403 /etc/haproxy/errors/403.http
	errorfile 408 /etc/haproxy/errors/408.http
	errorfile 500 /etc/haproxy/errors/500.http
	errorfile 502 /etc/haproxy/errors/502.http
	errorfile 503 /etc/haproxy/errors/503.http
	errorfile 504 /etc/haproxy/errors/504.http

frontend localnodes

	bind *:80
	mode http
	default_backend nodes

backend nodes

	mode http
	balance roundrobin
	option forwardfor
	http-request set-header X-Forwarded-Port %[dst_port]
	http-request add-header X-Forwarded-Proto https if { ssl_fc }
	option httpchk HEAD / HTTP/1.1\r\nHost:localhost
	server web01 127.0.0.1:9000 check
	server web02 127.0.0.1:9001 check
	server web03 127.0.0.1:9002 check

# Admin web page

	listen stats
	bind *:1338
	stats enable
	stats uri /
	stats hide-version
	stats auth admin:dietpi
_EOF_
		fi

		software_id=35 # Logitech Media Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Remove bundled SysVinit service
			systemctl disable --now logitechmediaserver &> /dev/null
			killall -qwr squeezeboxserver* # Provided service may not wait for exit, kill: https://github.com/MichaIng/DietPi/issues/1613#issuecomment-372787574
			[[ -f '/etc/init.d/logitechmediaserver' ]] && rm /etc/init.d/logitechmediaserver
			update-rc.d -f logitechmediaserver remove
			[[ -f '/etc/default/logitechmediaserver' ]] && rm /etc/default/logitechmediaserver

			# Grant user access to DietPi media files
			G_EXEC usermod -aG dietpi squeezeboxserver

			# systemd service
			cat << _EOF_ > /etc/systemd/system/logitechmediaserver.service
[Unit]
Description=Logitech Media Server (DietPi)

[Service]
User=squeezeboxserver
ExecStart=$(command -v squeezeboxserver) --prefsdir /var/lib/squeezeboxserver/prefs --logdir /var/log/squeezeboxserver/ --cachedir /var/lib/squeezeboxserver/cache --charset=utf8 --logfile /var/log/squeezeboxserver/error.log
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
_EOF_
			# Grab our test media for user
			Download_Test_Media

		fi

		software_id=55 # Wordpress
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Permissions
			G_EXEC chown -R www-data:www-data /var/www/wordpress

			# Create MariaDB database
			/boot/dietpi/func/create_mysql_db wordpress wordpress "$GLOBAL_PW"

		fi

		software_id=38 # FreshRSS
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Enable required PHP modules: https://github.com/FreshRSS/FreshRSS#requirements
			G_EXEC phpenmod curl gmp intl json pdo_mysql ctype dom mbstring xml zip

			# Apache configuration
			if (( ${aSOFTWARE_INSTALL_STATE[83]} > 0 )); then

				# Required modules
				a2enmod headers expires rewrite

				# Better compatibility with mobile clients
				echo 'AllowEncodedSlashes On' > /etc/apache2/conf-available/dietpi-freshrss.conf
				a2enconf dietpi-freshrss

			fi

			# Create MariaDB database and user
			if [[ -d '/mnt/dietpi_userdata/mysql/freshrss' ]]; then

				G_DIETPI-NOTIFY 2 'FreshRSS MariaDB database found, will NOT overwrite.'
				G_EXEC systemctl restart mariadb

			else

				/boot/dietpi/func/create_mysql_db freshrss freshrss "$GLOBAL_PW"

			fi

			# Set permissions for webserver access
			chown -R :www-data /opt/FreshRSS
			chmod -R g+r+w /opt/FreshRSS

			# CLI install: https://github.com/FreshRSS/FreshRSS/blob/master/cli/README.md#commands
			G_EXEC cd /opt/FreshRSS
			sudo -u www-data php ./cli/prepare.php
			sudo -u www-data php ./cli/do-install.php --default_user dietpi --auth_type form --environment production --title FreshRSS --db-type mysql --db-host localhost --db-user freshrss --db-password "$GLOBAL_PW" --db-base freshrss --db-prefix freshrss
			sudo -u www-data php ./cli/create-user.php --user dietpi --password "$GLOBAL_PW" --api_password "$GLOBAL_PW"
			G_EXEC cd /tmp/$G_PROGRAM_NAME

			# Link web interface to webroot
			ln -sf /opt/FreshRSS/p /var/www/freshrss

			# Create cron job for feed update every 30 minutes, if it does not yet exist
			crontab -u www-data -l 2>/dev/null | grep -q '/opt/FreshRSS/app/actualize_script.php' || ( crontab -u www-data -l 2>/dev/null ; echo '*/30 * * * * php /opt/FreshRSS/app/actualize_script.php' ) | crontab -u www-data -

		fi

		# TigerVNC/RealVNC Server - Shared setup
		if (( ${aSOFTWARE_INSTALL_STATE[28]} == 1 || ${aSOFTWARE_INSTALL_STATE[120]} == 1 )); then

			(( ${aSOFTWARE_INSTALL_STATE[28]} == 1 )) && software_id=28 || software_id=120
			Banner_Configuration

			# Service
			cat << '_EOF_' > /etc/systemd/system/vncserver.service
[Unit]
Description=Manage VNC Server (DietPi)
Before=xrdp.service xrdp-sesman.service
Wants=network-online.target
After=network-online.target dietpi-boot.service

[Service]
RemainAfterExit=yes
User=root
PAMName=login
Environment=HOME=/root
ExecStart=/usr/local/bin/vncserver start
ExecStop=/usr/local/bin/vncserver stop

[Install]
WantedBy=multi-user.target
_EOF_
			G_EXEC systemctl daemon-reload
			G_EXEC systemctl enable vncserver
			aSTART_SERVICES+=('vncserver')

			cat << '_EOF_' > /usr/local/bin/vncserver
#!/bin/bash

# Shared or virtual desktop?
SHARED_MODE=$(grep -cm1 '^[[:blank:]]*SOFTWARE_VNCSERVER_SHARE_DESKTOP=1' /boot/dietpi.txt)

# RealVNC or TigerVNC?
if [[ -f '/usr/bin/vncserver-virtual' ]]; then

	REALVNC=1
	FP_BINARY='/usr/bin/vncserver-virtual'
	# Set shared desktop mode if autostart is enabled for desktops. This prevents another VNC server being launched on :1.
	[[ -f '/boot/dietpi/.dietpi-autostart_index' && $(</boot/dietpi/.dietpi-autostart_index) == 2 ]] && SHARED_MODE=1

elif [[ -f '/usr/bin/tigervncserver' ]]; then

	REALVNC=0
	FP_BINARY='/usr/bin/tigervncserver'

else

	echo '[FAILED] No supported VNC server installed'
	exit 1

fi

case "$1" in

	start)
		# Virtual desktop mode
		if (( ! $SHARED_MODE )); then

			WIDTH=$(sed -n '/^[[:blank:]]*SOFTWARE_VNCSERVER_WIDTH=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
			HEIGHT=$(sed -n '/^[[:blank:]]*SOFTWARE_VNCSERVER_HEIGHT=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
			DEPTH=$(sed -n '/^[[:blank:]]*SOFTWARE_VNCSERVER_DEPTH=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
			DISPLAY=$(sed -n '/^[[:blank:]]*SOFTWARE_VNCSERVER_DISPLAY_INDEX=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
			$FP_BINARY :${DISPLAY:-1} -geometry ${WIDTH:-1280}'x'${HEIGHT:-720} -depth ${DEPTH:-16} || exit 1

		# TigerVNC shared desktop mode
		elif (( ! $REALVNC )); then

			echo 'Waiting for X11 to start...'
			until pgrep Xorg; do sleep 2; done

			sleep 5 # Give system some time to finish setting up X11 + desktop
			xset dpms force on # Disable screen blanking
			x11vnc -display :0 -usepw -forever || exit 1

		# RealVNC shared desktop mode
		else

			# No need to wait for X, this service detects the first appearing X session and attaches to it.
			systemctl start vncserver-x11-serviced || exit 1

		fi
	;;

	stop)
		DISPLAY=$(sed -n '/^[[:blank:]]*SOFTWARE_VNCSERVER_DISPLAY_INDEX=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
		$FP_BINARY -kill :${DISPLAY:-1} 2> /dev/null
		(( $REALVNC )) && systemctl stop vncserver-x11-serviced
		killall -qw x11vnc Xtigervnc /usr/bin/vncserver-x11-serviced vncserver-x11-serviced Xvnc-core
	;;

esac

exit 0
_EOF_
			G_EXEC chmod +x /usr/local/bin/vncserver

			# Stretch + TigerVNC: Disable Localhost only by default
			# shellcheck disable=SC2016
			[[ -f '/etc/vnc.conf' ]] && GCI_PRESERVE=1 G_CONFIG_INJECT '\$localhost[[:blank:]]*=' '$localhost = "no";' /etc/vnc.conf

			local cmd_launch_desktop=
			# LXDE
			if (( ${aSOFTWARE_INSTALL_STATE[23]} > 0 )); then

				cmd_launch_desktop='lxsession -s LXDE -e LXDE'

			# MATE
			elif (( ${aSOFTWARE_INSTALL_STATE[24]} > 0 )); then

				cmd_launch_desktop='mate-session'

			# GNUstep
			elif (( ${aSOFTWARE_INSTALL_STATE[26]} > 0 )); then

				cmd_launch_desktop='x-window-manager'

			# Xfce
			elif (( ${aSOFTWARE_INSTALL_STATE[25]} > 0 )); then

				cmd_launch_desktop='xfce4-session'

			# LXQt
			elif (( ${aSOFTWARE_INSTALL_STATE[173]} > 0 )); then

				cmd_launch_desktop='startlxqt'

			fi

			G_EXEC mkdir -p /root/.vnc
			cat << _EOF_ > /root/.vnc/xstartup
#!/bin/dash
export SHELL='/bin/bash'
[ -x '/etc/vnc/xstartup' ] && exec /etc/vnc/xstartup
[ -r '/root/.Xresources' ] && xrdb /root/.Xresources
xsetroot -solid grey
vncconfig -iconic &
exec $cmd_launch_desktop
_EOF_
			G_EXEC chmod +x /root/.vnc/xstartup

			# TigerVNC: Set control + read-only passwords (RealVNC uses UNIX user authentication by default, does not support the "-f" option and uses different files!)
			if command -v tigervncpasswd > /dev/null; then

				tigervncpasswd -f << _EOF_ > /root/.vnc/passwd
$GLOBAL_PW
$GLOBAL_PW
_EOF_
				G_EXEC chmod 600 /root/.vnc/passwd

			fi

		fi

		software_id=74 # InfluxDB
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Move DB/datadir to userdata location
			[[ -d '/mnt/dietpi_userdata/influxdb' ]] || mv /var/lib/influxdb /mnt/dietpi_userdata/
			rm -Rf /var/lib/influxdb
			ln -sf /mnt/dietpi_userdata/influxdb /var/lib/influxdb

			# Permissions
			chown -R influxdb:influxdb /mnt/dietpi_userdata/influxdb

		fi

		software_id=77 # Grafana
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Remove obsolete sysvinit service
			[[ -f '/etc/init.d/grafana-server' ]] && rm /etc/init.d/grafana-server
			update-rc.d -f grafana-server remove

			# Link DB/plugins to userdata location
			if [[ -d '/mnt/dietpi_userdata/grafana' ]]; then

				G_DIETPI-NOTIFY 2 "Existing database/plugin directory /mnt/dietpi_userdata/grafana found. Will not overwrite..."

			elif [[ -d '/var/lib/grafana' ]]; then

				mv /var/lib/grafana /mnt/dietpi_userdata/

			else

				mkdir -p /mnt/dietpi_userdata/grafana
				chown -R grafana:grafana /mnt/dietpi_userdata/grafana

			fi
			rm -Rf /var/lib/grafana
			ln -s /mnt/dietpi_userdata/grafana /var/lib/grafana

			# Config: Apply our defaults only if nothing was set before
			# - Set password, wrap into trippled double quotes in case of ; or # being contained, according to docs: http://docs.grafana.org/installation/configuration/#password
			GCI_PRESERVE=1 GCI_PASSWORD=1 G_CONFIG_INJECT 'admin_password[[:blank:]]*=' "admin_password = \"\"\"$GLOBAL_PW\"\"\"" /etc/grafana/grafana.ini
			# - Set port to 3001 (away from default 3000) to avoid conflict with Gogs and Gitea
			GCI_PRESERVE=1 G_CONFIG_INJECT 'http_port[[:blank:]]*=' 'http_port = 3001' /etc/grafana/grafana.ini

		fi

		software_id=80 # Ubooquity
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# User
			Create_User -G dietpi -d /mnt/dietpi_userdata/ubooquity ubooquity

			# Data
			G_EXEC mkdir -p /mnt/dietpi_userdata/{ebooks,comics}

			# Logs
			G_EXEC rm -Rf /mnt/dietpi_userdata/ubooquity/logs /var/log/ubooquity
			G_EXEC mkdir -p /var/log/ubooquity
			G_EXEC ln -s /var/log/ubooquity /mnt/dietpi_userdata/ubooquity/logs

			# Optimise memory limit
			local memory_limit=$(( $RAM_PHYS / 5 ))
			(( $memory_limit < 200 )) && memory_limit=200

			# Service
			G_DIETPI-NOTIFY 2 "Generating systemd service to start ${aSOFTWARE_NAME[$software_id]} on boot"
			cat << _EOF_ > /etc/systemd/system/ubooquity.service
[Unit]
Description=Ubooquity (DietPi)
Wants=network-online.target
After=network-online.target dietpi-boot.service

[Service]
User=ubooquity
WorkingDirectory=/mnt/dietpi_userdata/ubooquity
ExecStart=$(command -v java) -Xmx${memory_limit}m -jar /mnt/dietpi_userdata/ubooquity/Ubooquity.jar --headless --remoteadmin --adminport 2038 --libraryport 2039

[Install]
WantedBy=multi-user.target
_EOF_
			# Permissions
			G_EXEC chmod +x /mnt/dietpi_userdata/ubooquity/Ubooquity.jar
			G_EXEC chown -R ubooquity /{mnt/dietpi_userdata,var/log}/ubooquity
			G_EXEC chown ubooquity:dietpi /mnt/dietpi_userdata/{ebooks,comics}
			G_EXEC chmod 775 /mnt/dietpi_userdata/{ebooks,comics}

		fi

		software_id=179 # Komga
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# User
			Create_User -G dietpi -d /mnt/dietpi_userdata/komga komga

			# Data
			G_EXEC mkdir -p /mnt/dietpi_userdata/{ebooks,comics}

			# Config
			[[ -f '/mnt/dietpi_userdata/komga/application.yml' ]] || cat << _EOF_ > /mnt/dietpi_userdata/komga/application.yml
komga:
  libraries-scan-startup: true # Scan libraries at startup
  libraries-scan-cron: "* 2 * * * *" # Scan libraries periodically every hour at :02
  database:
    file: /mnt/dietpi_userdata/komga/database.sqlite
  database-backup:
    enabled: true # Enable database backups
    startup: false # Do not create a backup at startup
    schedule: "* 22 4 * * *" # Create a backup every night at 4:22
    path: /mnt/dietpi_userdata/komga/database-backup.zip
  remember-me:
    key: $(openssl rand -hex 32)
logging:
  file:
    name: "" # Disable file logging, use: "journalctl -u komga"
  level:
    root: WARN # TRACE DEBUG INFO WARN ERROR
server:
  port: 2037
spring:
  datasource:
    url: jdbc:h2:/mnt/dietpi_userdata/komga/database.h2
_EOF_
			# Optimise memory limit
			local memory_limit=$(( $RAM_PHYS / 5 ))
			(( $memory_limit < 200 )) && memory_limit=200

			# Service
			cat << _EOF_ > /etc/systemd/system/komga.service
[Unit]
Description=Komga (DietPi)
Wants=network-online.target
After=network-online.target dietpi-boot.service

[Service]
User=komga
WorkingDirectory=/mnt/dietpi_userdata/komga
ExecStart=$(command -v java) -Xmx${memory_limit}m -jar komga.jar

[Install]
WantedBy=multi-user.target
_EOF_
			# Permissions
			G_EXEC chmod +x /mnt/dietpi_userdata/komga/komga.jar
			G_EXEC chown -R komga /mnt/dietpi_userdata/komga
			G_EXEC chown komga:dietpi /mnt/dietpi_userdata/{ebooks,comics}
			G_EXEC chmod 775 /mnt/dietpi_userdata/{ebooks,comics}

		fi

		software_id=64 # phpSysInfo
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration
			[[ -f '/var/www/phpsysinfo/phpsysinfo.ini' ]] || dps_index=$software_id Download_Install 'phpsysinfo.ini' /var/www/phpsysinfo/phpsysinfo.ini

		fi

		software_id=56 # Single File PHP Gallery
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Enable required PHP modules: https://sye.dk/sfpg/
			G_EXEC phpenmod gd

			# Get test images
			G_EXEC mkdir -p /var/www/gallery/DietPi
			G_EXEC curl -sSfL https://dietpi.com/images/dietpi-logo_256x256.png -o /var/www/gallery/DietPi/logo_256.png

			G_EXEC mkdir -p /var/www/gallery/Tr-Zero
			G_EXEC curl -sSfL https://media.indiedb.com/images/games/1/25/24673/SS_0.jpg -o /var/www/gallery/Tr-Zero/SS_0.jpg
			G_EXEC curl -sSfL https://media.indiedb.com/images/games/1/25/24673/SS_44.jpg -o /var/www/gallery/Tr-Zero/SS_1.jpg
			G_EXEC curl -sSfL https://media.indiedb.com/images/games/1/25/24673/3.png -o /var/www/gallery/Tr-Zero/SS_2.jpg

			# Permissions for cache/thumbnail/database
			G_EXEC mkdir -p /var/www/gallery/_sfpg_data
			G_EXEC chown www-data:www-data /var/www/gallery/_sfpg_data

			# Enable (Some type of security trigger)
			G_EXEC sed -i "/define('SECURITY_PHRASE'/c\\\tdefine('SECURITY_PHRASE', 'true');" /var/www/gallery/index.php

		fi

		software_id=40 # Ampache
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration
			Download_Test_Media

			# Import our pre-made Ampache database, if not existent already
			if [[ ! -d '/mnt/dietpi_userdata/mysql/ampache' ]]; then

				/boot/dietpi/func/create_mysql_db ampache ampache "$GLOBAL_PW"
				Download_Install 'https://dietpi.com/downloads/mysql_databases/ampache_mysql_3.8.2-v6.0.zip'
				G_EXEC mysql ampache < ampache.sql
				G_EXEC_NOHALT=1 G_EXEC rm ampache.sql
				# Also update password here for rare but possible case that database was lost but config file still exists
				[[ -f '/var/www/ampache/config/ampache.cfg.php' ]] && G_CONFIG_INJECT 'database_password =' "database_password = \"$GLOBAL_PW\"" /var/www/ampache/config/ampache.cfg.php

			fi

			# Create new config, if not existent already
			if [[ ! -f '/var/www/ampache/config/ampache.cfg.php' ]]; then

				G_EXEC mv /var/www/ampache/config/ampache.cfg.php{.dist,}
				G_CONFIG_INJECT 'web_path[[:blank:]]=' 'web_path = "/ampache"' /var/www/ampache/config/ampache.cfg.php
				G_CONFIG_INJECT 'database_hostname[[:blank:]]=' 'database_hostname = localhost' /var/www/ampache/config/ampache.cfg.php
				G_CONFIG_INJECT 'database_port[[:blank:]]=' 'database_port = ""' /var/www/ampache/config/ampache.cfg.php
				G_CONFIG_INJECT 'database_name[[:blank:]]=' 'database_name = ampache' /var/www/ampache/config/ampache.cfg.php
				G_CONFIG_INJECT 'database_username[[:blank:]]=' 'database_username = ampache' /var/www/ampache/config/ampache.cfg.php
				G_CONFIG_INJECT 'database_password[[:blank:]]=' "database_password = \"$GLOBAL_PW\"" /var/www/ampache/config/ampache.cfg.php
				G_CONFIG_INJECT 'waveform[[:blank:]]=' 'waveform = "true"' /var/www/ampache/config/ampache.cfg.php
				G_CONFIG_INJECT 'tmp_dir_path[[:blank:]]=' 'tmp_dir_path = "/tmp"' /var/www/ampache/config/ampache.cfg.php
				G_CONFIG_INJECT 'generate_video_preview[[:blank:]]=' 'generate_video_preview = "true"' /var/www/ampache/config/ampache.cfg.php
				G_CONFIG_INJECT 'channel[[:blank:]]=' 'channel = "true"' /var/www/ampache/config/ampache.cfg.php
				G_CONFIG_INJECT 'transcode_m4a[[:blank:]]=' 'transcode_m4a = "required"' /var/www/ampache/config/ampache.cfg.php
				G_CONFIG_INJECT 'transcode_flac[[:blank:]]=' 'transcode_flac = "required"' /var/www/ampache/config/ampache.cfg.php
				G_CONFIG_INJECT 'transcode_mpc[[:blank:]]=' 'transcode_mpc = "required"' /var/www/ampache/config/ampache.cfg.php
				G_CONFIG_INJECT 'transcode_ogg[[:blank:]]=' 'transcode_ogg = "allowed"' /var/www/ampache/config/ampache.cfg.php
				G_CONFIG_INJECT 'transcode_wav[[:blank:]]=' 'transcode_wav = "required"' /var/www/ampache/config/ampache.cfg.php
				G_CONFIG_INJECT 'transcode_avi[[:blank:]]=' 'transcode_avi = "allowed"' /var/www/ampache/config/ampache.cfg.php
				G_CONFIG_INJECT 'transcode_mkv[[:blank:]]=' 'transcode_mkv = "allowed"' /var/www/ampache/config/ampache.cfg.php
				G_CONFIG_INJECT 'transcode_mpg[[:blank:]]=' 'transcode_mpg = "allowed"' /var/www/ampache/config/ampache.cfg.php
				G_CONFIG_INJECT 'encode_target[[:blank:]]=' 'encode_target = mp3' /var/www/ampache/config/ampache.cfg.php
				G_CONFIG_INJECT 'encode_video_target[[:blank:]]=' 'encode_video_target = webm' /var/www/ampache/config/ampache.cfg.php
				G_CONFIG_INJECT 'transcode_cmd[[:blank:]]=' 'transcode_cmd = "ffmpeg"' /var/www/ampache/config/ampache.cfg.php

			fi
			
			# Permissions: Ampache can automatically migrate old configs to the new config file
			G_EXEC chown www-data /var/www/ampache/config/ampache.cfg.php

		fi

		software_id=97 # OpenVPN Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Create initial server and client configs, if not yet present
			# - Wildcard config file detection via for loop
			local i
			for i in /etc/openvpn/*.conf
			do

				# Inform user, if config exist, that config generation is skipped
				if [[ -f $i ]]; then

					G_DIETPI-NOTIFY 2 "Existing OpenVPN configuration found: $i
 - Config generation will be skipped.
 - If you need a fresh key/cert/config set, please remove $i
   and run: dietpi-software reinstall 97"
					break

				fi

				# Pre-v6.26 cleanup
				[[ -f '/etc/openvpn/dh2048.pem' ]] && rm /etc/openvpn/dh2048.pem
				dpkg-query -s easy-rsa &> /dev/null && G_AGP easy-rsa

				# Download latest easy-rsa from GitHub
				G_DIETPI-NOTIFY 2 'Downloading latest easy-rsa for certificate and key generation...'
				local fallback_url='https://github.com/OpenVPN/easy-rsa/releases/download/v3.0.8/EasyRSA-3.0.8.tgz'
				Download_Install "$(curl -sSfL 'https://api.github.com/repos/OpenVPN/easy-rsa/releases/latest' | mawk -F\" '/"browser_download_url": .*\/EasyRSA-[^"\/]*\.tgz"/{print $4}')"
				[[ -d '/etc/openvpn/easy-rsa' ]] && rm -R /etc/openvpn/easy-rsa
				G_EXEC mv EasyRSA-* /etc/openvpn/easy-rsa

				# Cert and key generation via easy-rsa
				G_DIETPI-NOTIFY 2 'Generating unique OpenVPN certificates and keys. Please wait...\n'
				G_EXEC cd /etc/openvpn/easy-rsa
				cat << '_EOF_' > vars
set_var EASYRSA_REQ_COUNTRY	"UK"
set_var EASYRSA_REQ_PROVINCE	"DietPi"
set_var EASYRSA_REQ_CITY	"DietPi"
set_var EASYRSA_REQ_ORG		"DietPi"
set_var EASYRSA_REQ_EMAIL	"noreply@dietpi.com"
set_var EASYRSA_REQ_OU		"DietPi"
set_var EASYRSA_BATCH		"1"
_EOF_
				./easyrsa init-pki
				./easyrsa build-ca nopass
				./easyrsa gen-dh
				./easyrsa build-server-full DietPi_OpenVPN_Server nopass
				./easyrsa build-client-full DietPi_OpenVPN_Client nopass

				# Server config
				cp -a pki/{ca.crt,dh.pem,issued/DietPi_OpenVPN_Server.crt,private/DietPi_OpenVPN_Server.key} /etc/openvpn/
				G_EXEC cd /etc/openvpn
				cat << '_EOF_' > server.conf
port 1194
proto udp
dev tun

ca ca.crt
cert DietPi_OpenVPN_Server.crt
key DietPi_OpenVPN_Server.key
dh dh.pem

server 10.8.0.0 255.255.255.0

client-to-client
keepalive 10 60
comp-lzo
max-clients 10

user nobody
group nogroup

persist-key
persist-tun
verb 3

# Web Forwarding (uncomment to enable)
#push "redirect-gateway"
#push "dhcp-option DNS 10.8.0.1"
_EOF_

				# Client config
				cat << '_EOF_' > DietPi_OpenVPN_Client.ovpn
client
proto udp
dev tun

# IP/domain name of DietPi system, running OpenVPN server
remote mywebsite.com 1194

resolv-retry infinite
nobind

user nobody
group nogroup

persist-key
persist-tun

remote-cert-tls server
comp-lzo
verb 3
_EOF_
				# - Add CA cert, client cert and key
				echo -e "
<ca>\n$(<ca.crt)\n</ca>
<cert>\n$(<easy-rsa/pki/issued/DietPi_OpenVPN_Client.crt)\n</cert>
<key>\n$(<easy-rsa/pki/private/DietPi_OpenVPN_Client.key)\n</key>" >> DietPi_OpenVPN_Client.ovpn

				# - Copy to userdata
				G_EXEC cp DietPi_OpenVPN_Client.ovpn /mnt/dietpi_userdata/
				# - and /boot partition
				G_EXEC cp DietPi_OpenVPN_Client.ovpn /boot/

				G_EXEC cd /tmp/$G_PROGRAM_NAME
				break # Always break loop which is only for single wildcard file detection

			done

			# Enable IP forwarding
			echo -e 'net.ipv4.ip_forward=1\nnet.ipv6.conf.all.forwarding=1\nnet.ipv6.conf.default.forwarding=1' > /etc/sysctl.d/dietpi-openvpn.conf
			sysctl net.ipv4.ip_forward=1 net.ipv6.conf.all.forwarding=1 net.ipv6.conf.default.forwarding=1

			# Web Fowarding (Setup IPtables, must also be run during boot)
			#iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o "$(mawk 'NR==3' /run/dietpi/.network)" -j MASQUERADE

			aSTART_SERVICES+=('openvpn')

		fi

		software_id=172 # WireGuard
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Try to enable module, if it fails, a reboot is required
			local module_active=0
			modprobe wireguard 2> /dev/null && module_active=1

			# Server/Client choice
			local choice_required=
			while :
			do

				G_WHIP_MENU_ARRAY=(

					'Server' ': Use this machine as VPN server and allow clients to connect to it.'
					'Client' ': Use this machine as VPN client to connect to another VPN server or service provider.'

				)

				G_WHIP_MENU "${choice_required}Please choose, if this machine should be set up as VPN server or client:" && break
				choice_required='[ERROR] A choice is required to finish the WireGuard install.\n\n'

			done

			# Server choice
			if [[ $G_WHIP_RETURNED_VALUE == 'Server' ]]; then

				# Public IP/domain and desired WireGuard server port
				local invalid_text=
				local domain=$(hostname -f)
				while :
				do

					G_WHIP_DEFAULT_ITEM=$domain
					if G_WHIP_INPUTBOX "${invalid_text}Please enter your servers public IP/domain for WireGuard client access:" && [[ $G_WHIP_RETURNED_VALUE ]]; then

						domain=${G_WHIP_RETURNED_VALUE#http*://}
						break

					else

						invalid_text='[ERROR] No valid entry found. Please retry...\n\n'

					fi

				done
				invalid_text=
				local port=51820
				while :
				do

					G_WHIP_DEFAULT_ITEM=$port
					if G_WHIP_INPUTBOX "${invalid_text}Please enter the network port, that should be used to access your WireGuard server:\n
NB: This port needs to be forwarded by your router and/or opened in your firewall settings. Default value is: 51820" && disable_error=1 G_CHECK_VALIDINT "$G_WHIP_RETURNED_VALUE" 0; then

						port=$G_WHIP_RETURNED_VALUE
						break

					else

						invalid_text='[ERROR] No valid entry found, value needs to be a sequence of integers. Please retry...\n\n'

					fi

				done

				# Create everything inside WireGuard config dir
				G_EXEC cd /etc/wireguard

				# For security reasons set umask to 077
				umask 077

				# Create server and client keys
				[[ -f 'server_private.key' ]] || wg genkey > server_private.key
				[[ -f 'server_public.key' ]] || wg pubkey < server_private.key > server_public.key
				[[ -f 'client_private.key' ]] || wg genkey > client_private.key
				[[ -f 'client_public.key' ]] || wg pubkey < client_private.key > client_public.key

				# Server config
				[[ -f 'wg0.conf' ]] || cat << _EOF_ > wg0.conf
[Interface]
Address = 10.9.0.1/24
PrivateKey = $(<server_private.key)
ListenPort = $port

PreUp = /boot/dietpi/func/obtain_network_details
PostUp = sysctl net.ipv4.conf.%i.forwarding=1 net.ipv4.conf.\$(mawk 'NR==3' /run/dietpi/.network).forwarding=1
PostUp = sysctl net.ipv6.conf.\$(mawk 'NR==3' /run/dietpi/.network).accept_ra=2
PostUp = sysctl net.ipv6.conf.%i.forwarding=1 net.ipv6.conf.\$(mawk 'NR==3' /run/dietpi/.network).forwarding=1
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o \$(mawk 'NR==3' /run/dietpi/.network) -j MASQUERADE
PostUp = ip6tables -A FORWARD -i %i -j ACCEPT; ip6tables -t nat -A POSTROUTING -o \$(mawk 'NR==3' /run/dietpi/.network) -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o \$(mawk 'NR==3' /run/dietpi/.network) -j MASQUERADE
PostDown = ip6tables -D FORWARD -i %i -j ACCEPT; ip6tables -t nat -D POSTROUTING -o \$(mawk 'NR==3' /run/dietpi/.network) -j MASQUERADE

# Client 1
[Peer]
PublicKey = $(<client_public.key)
AllowedIPs = 10.9.0.2/32

# Client 2
#[Peer]
#PublicKey = XXXX
#AllowedIPs = 10.9.0.3/32
_EOF_

				# Server local network IP
				local server_ip=$(mawk 'NR==4' /run/dietpi/.network)

				# Server DNS nameserver
				local server_dns=$(mawk '/nameserver/{print $2;exit}' /etc/resolv.conf)
				# - Replace "127.0.0.1"/"localhost" loopback entries by server wg0 IP: https://github.com/MichaIng/DietPi/issues/2482
				server_dns=${server_dns//127.0.0.1/10.9.0.1}
				server_dns=${server_dns//localhost/10.9.0.1}

				# Client config
				[[ -f 'wg0-client.conf' ]] || cat << _EOF_ > wg0-client.conf
[Interface]
Address = 10.9.0.2/24
PrivateKey = $(<client_private.key)

# Comment the following to preserve the clients default DNS server, or force a desired one.
DNS = $server_dns

# Kill switch: Uncomment the following, if the client should stop any network traffic, when disconnected from the VPN server
# NB: This requires "iptables" to be installed, thus will not work on most mobile phones.
#PostUp = iptables -I OUTPUT ! -o %i -m mark ! --mark \$(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT && ip6tables -I OUTPUT ! -o %i -m mark ! --mark \$(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT
#PreDown = iptables -D OUTPUT ! -o %i -m mark ! --mark \$(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT && ip6tables -D OUTPUT ! -o %i -m mark ! --mark \$(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT

[Peer]
PublicKey = $(<server_public.key)
# Tunnel all network traffic through the VPN:
#	AllowedIPs = 0.0.0.0/0, ::/0
# Tunnel access to server-side local network only:
#	AllowedIPs = ${server_ip%.*}.0/24
# Tunnel access to VPN server only:
#	AllowedIPs = $server_ip/32
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = $domain:$port

# Uncomment the following, if you're behind a NAT and want the connection to be kept alive.
#PersistentKeepalive = 25
_EOF_
				# Set umask back to default 022
				umask 022

				# Navigate back to DietPi-Software working dir
				G_EXEC cd /tmp/$G_PROGRAM_NAME

				# Use wg-quick@.service to start VPN on boot
				# - Assure DietPi-Boot has finished first: https://github.com/MichaIng/DietPi/issues/3175#issuecomment-544607657
				mkdir -p /etc/systemd/system/wg-quick@wg0.service.d
				echo -e '[Unit]\nAfter=dietpi-boot.service' > /etc/systemd/system/wg-quick@wg0.service.d/dietpi.conf
				systemctl daemon-reload
				systemctl enable wg-quick@wg0
				# - Start now if module is active already
				(( $module_active )) && systemctl start wg-quick@wg0

			# Client choice
			elif [[ $G_WHIP_RETURNED_VALUE == 'Client' ]]; then

				G_AGI resolvconf # Use by wg-quick to apply DNS in client setup
				G_WHIP_MSG '[ INFO ] WireGuard client setup has been chosen\n
Please follow the instructions of your VPN provider to configure WireGuard.\n
If no WireGuard (auto)start is included, but you require it, please do the following:
1. Check for the created config file/interface name:
	ls -Al /etc/wireguard/
   It has a ".conf" file ending, lets assume "wg0-client.conf".
2. To start the VPN interface, run:
	systemctl start wg-quick@wg0-client
3. To autostart the VPN interface on boot, run:
	systemctl enable wg-quick@wg0-client
4. To disable autostart again, run:
	systemctl disable wg-quick@wg0-client'

			fi

			(( $module_active )) || G_WHIP_MSG '[ INFO ] WireGuard install finished\n\nNB: WireGuard has been successfully installed, but a reboot is required, before it can be started.'

			unset -v module_active choice_required domain port invalid_text server_ip

		fi

		software_id=60 # WiFi Hotspot
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			local eth_index=$(mawk 'NR==1' /run/dietpi/.network)
			local wifi_index=$(mawk 'NR==2' /run/dietpi/.network)

			# DHCPD Config
			G_BACKUP_FP /etc/dhcp/dhcpd.conf
			cat << '_EOF_' > /etc/dhcp/dhcpd.conf
ddns-update-style none;
default-lease-time 600;
max-lease-time 7200;
authoritative;
log-facility local7;

subnet 192.168.42.0 netmask 255.255.255.0 {
        range 192.168.42.10 192.168.42.50;
        option broadcast-address 192.168.42.255;
        option routers 192.168.42.1;
        option domain-name "local";
        option domain-name-servers 8.8.8.8, 8.8.4.4;
}
_EOF_

			# Assign detected WLAN interface
			echo "INTERFACESv4=\"wlan$wifi_index\"" > /etc/default/isc-dhcp-server

			# Remove all entries below wlan, so we can recreate them
			G_EXEC sed -i '/allow-hotplug wlan/q0' /etc/network/interfaces

			# Enable up wlan
			G_CONFIG_INJECT 'allow-hotplug wlan' "allow-hotplug wlan$wifi_index" /etc/network/interfaces

			# Add WiFi settings to network interfaces config
			cat << _EOF_ >> /etc/network/interfaces
iface wlan$wifi_index inet static
address 192.168.42.1
netmask 255.255.255.0
#gateway 192.168.0.1
wireless-power off
#dns-nameservers 8.8.8.8 8.8.4.4

# IP tables
up iptables-restore < /etc/iptables.ipv4.nat
_EOF_
			# Assign static IP for wlan now
			ip a add 192.168.42.1 dev "wlan$wifi_index"

			# Create access point config
			G_BACKUP_FP /etc/hostapd/hostapd.conf
			cat << _EOF_ > /etc/hostapd/hostapd.conf
interface=wlan$wifi_index
driver=nl80211
ssid=$(sed -n '/^[[:blank:]]*SOFTWARE_WIFI_HOTSPOT_SSID=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
hw_mode=g
channel=$(sed -n '/^[[:blank:]]*SOFTWARE_WIFI_HOTSPOT_CHANNEL=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
macaddr_acl=0
auth_algs=1
ignore_broadcast_ssid=0
wpa=2
wpa_passphrase=$(sed -n '/^[[:blank:]]*SOFTWARE_WIFI_HOTSPOT_KEY=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
wpa_key_mgmt=WPA-PSK
wpa_pairwise=TKIP
rsn_pairwise=CCMP
_EOF_
			# Check for RTL8188C* device, use the patched driver with compiled binary: https://github.com/pritambaral/hostapd-rtl871xdrv#why
			# - Stay with default driver on RPi, where the issue seems to have been resolved differently and "rtl871xdrv" is not supported: https://github.com/MichaIng/DietPi/issues/2531#issuecomment-650810121
			(( $WIFIHOTSPOT_RTL8188C_DEVICE && $G_HW_MODEL > 9 && $G_HW_ARCH < 4 || $WIFIHOTSPOT_RTL8188C_PACKAGE )) && G_CONFIG_INJECT 'driver=' 'driver=rtl871xdrv' /etc/hostapd/hostapd.conf

			# Enable access point config
			echo "DAEMON_CONF=\"/etc/hostapd/hostapd.conf\"" > /etc/default/hostapd

			# Enable IP forwarding
			echo -e 'net.ipv4.ip_forward=1\nnet.ipv6.conf.all.forwarding=1\nnet.ipv6.conf.default.forwarding=1' > /etc/sysctl.d/dietpi-wifihotspot.conf
			sysctl net.ipv4.ip_forward=1 net.ipv6.conf.all.forwarding=1 net.ipv6.conf.default.forwarding=1

			# Apply IP tables
			iptables -t nat -A POSTROUTING -o "eth$eth_index" -j MASQUERADE
			iptables -A FORWARD -i "eth$eth_index" -o "wlan$wifi_index" -m state --state RELATED,ESTABLISHED -j ACCEPT
			iptables -A FORWARD -i "wlan$wifi_index" -o "eth$eth_index" -j ACCEPT

			# Save IP tables, applied during ifup in /etc/network/interfaces
			iptables-save > /etc/iptables.ipv4.nat

			# RPi3/4 onboard WiFi: Enable N support
			[[ $G_HW_MODEL == [34] ]] && G_CONFIG_INJECT 'ieee80211n=' 'ieee80211n=1' /etc/hostapd/hostapd.conf

		fi

		software_id=61 # Tor Hotspot
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Remove obsolete sysvinit service and config
			[[ -f '/etc/init.d/tor' ]] && rm /etc/init.d/tor
			update-rc.d -f tor remove
			[[ -f '/etc/default/tor' ]] && rm /etc/default/tor

			# Tor config
			cat << _EOF_ > /etc/tor/torrc
Log notice stdout
VirtualAddrNetwork 10.192.0.0/10
AutomapHostsSuffixes .onion,.exit
AutomapHostsOnResolve 1
TransPort 192.168.42.1:9040
DNSPort 192.168.42.1:53
_EOF_

			# Flush IP tables
			iptables -F
			iptables -t nat -F

			# Generate tor prerouting tables
			local wifi_index=$(mawk 'NR==2' /run/dietpi/.network)
			iptables -t nat -A PREROUTING -i "wlan$wifi_index" -p tcp --dport 22 -j REDIRECT --to-ports 22
			iptables -t nat -A PREROUTING -i "wlan$wifi_index" -p udp --dport 53 -j REDIRECT --to-ports 53
			iptables -t nat -A PREROUTING -i "wlan$wifi_index" -p tcp --syn -j REDIRECT --to-ports 9040

			# Save IP tables
			iptables-save > /etc/iptables.ipv4.nat

			# User: Test tor is functional
			#https://check.torproject.org

		fi

		software_id=48 # Pydio
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# PHP configuration
			G_EXEC phpenmod apcu gd intl pdo_mysql dom mbstring xml

			# Webserver config
			# - Apache2
			if (( ${aSOFTWARE_INSTALL_STATE[83]} > 0 )); then

				# Enable Apache2 rewrite engine
				a2enmod rewrite

				# Move Pydio Apache2 config in place
				dps_index=$software_id Download_Install 'apache.pydio.conf' /etc/apache2/sites-available/dietpi-pydio.conf
				a2ensite dietpi-pydio

			# - Lighttpd
			elif (( ${aSOFTWARE_INSTALL_STATE[84]} > 0 )); then

				# Enable Lighttpd setenv, access and rewrite modules
				G_CONFIG_INJECT '"mod_access",' '	"mod_access",' /etc/lighttpd/lighttpd.conf '"mod_.+",'
				G_CONFIG_INJECT '"mod_setenv",' '	"mod_setenv",' /etc/lighttpd/lighttpd.conf '"mod_.+",'

				# Move Pydio Lighttpd config in place
				dps_index=$software_id Download_Install 'lighttpd.pydio.conf' /etc/lighttpd/conf-available/99-dietpi-pydio.conf
				G_EXEC_POST_FUNC(){ [[ $exit_code == 2 ]] && exit_code=0; } # Do not fail if modules are enabled already
				G_EXEC lighty-enable-mod rewrite dietpi-pydio

			# - Nginx
			elif (( ${aSOFTWARE_INSTALL_STATE[85]} > 0 )); then

				# Move Pydio Nginx config in place
				dps_index=$software_id Download_Install 'nginx.pydio.conf' /etc/nginx/sites-dietpi/dietpi-pydio.conf

			fi

			# Create MySQL DB
			/boot/dietpi/func/create_mysql_db pydio pydio "$GLOBAL_PW"

			# Setup Data directory
			# - Skip if already existent
			local target_data_dir=/mnt/dietpi_userdata/pydio_data
			if [[ -d $target_data_dir ]]; then

				G_DIETPI-NOTIFY 2 "Existing $target_data_dir found, will migrate..."
				[[ -e '/var/www/pydio/data' ]] && rm -R /var/www/pydio/data

			else

				# - Move data structure
				[[ -e $target_data_dir ]] && rm -R $target_data_dir
				mv /var/www/pydio/data $target_data_dir

			fi

			# Create symlink
			ln -sf $target_data_dir /var/www/pydio/data
			chown -R www-data:www-data $target_data_dir

		fi

		software_id=99 # EmonPi
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Copy config
			cp /etc/emonhub/conf/default/emonhub /etc/default/emonhub

			# Setup service
			cp /etc/emonhub/service/emonhub /etc/init.d/emonhub
			chmod +x /etc/init.d/emonhub
			update-rc.d emonhub defaults

			chmod +x -R /etc/emonhub

			# Disable onboard Bluetooth, if present, to recover ttyAMA0
			if (( $G_HW_ONBOARD_WIFI )); then

				/boot/dietpi/func/dietpi-set_hardware bluetooth disable

			# Else, assure that primary UART is enabled, which is ttyAMA0 on non-onboard WiFi/BT models
			else

				G_CONFIG_INJECT 'enable_uart=' 'enable_uart=1' /boot/config.txt

			fi

			# Disable console on ttyAMA0
			/boot/dietpi/func/dietpi-set_hardware serialconsole disable ttyAMA0

			# Apply user API KEY
			USER_EMONHUB_APIKEY_CURRENT=$(sed -n '/^[[:blank:]]*SOFTWARE_EMONHUB_APIKEY=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
			sed -i "/apikey/c\        apikey = $USER_EMONHUB_APIKEY_CURRENT" /etc/emonhub/conf/emonhub.conf

		fi

		software_id=66 # RPi Monitor
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Update apt package stats
			/usr/share/rpimonitor/scripts/updatePackagesStatus.pl

			# USB drive stats implimentation by Rich
			if (( $USBDRIVE )); then

				sed -i '\/include=\/etc\/rpimonitor\/template\/sdcard.conf/a include=\/etc\/rpimonitor\/template\/usb_hdd.conf' /etc/rpimonitor/data.conf

				cat << _EOF_ > /etc/rpimonitor/template/usb_hdd.conf
########################################################################
# Extract USB HDD (sda1) information
#  Page: 1
#  Information               Status     Statistics
#  - USBHDD1 total          - yes      - yes
#  - USBHDD1 used           - yes      - yes
########################################################################
static.10.name=usbhdd_total
static.10.source=df -t ext4
static.10.regexp=sda1\s+(\d+)
static.10.postprocess=\$1/1024

dynamic.14.name=usbhdd_used
dynamic.14.source=df -t ext4
dynamic.14.regexp=sda1\s+\d+\s+(\d+)
dynamic.14.postprocess=\$1/1024
dynamic.14.rrd=GAUGE

web.status.1.content.9.name=USB HDD
web.status.1.content.9.icon=usb_hdd.png
web.status.1.content.9.line.1="<b>/sda1</b> Used: <b>"+KMG(data.usbhdd_used,'M')+"</b> (<b>"+Percent(data.udbhdd_used,data.usbhdd_total,'M')+"</b>) Free: <b>"+KMG(data.usbhdd_total-data.usbhdd_used,'M')+ "</b> Total: <b>"+ KMG(data.usbhdd_total,'M') +"</b>"
web.status.1.content.9.line.2=ProgressBar(data.usbhdd_used,data.usbhdd_total)

web.statistics.1.content.9.name=USB HDD
web.statistics.1.content.9.graph.1=usbhdd_total
web.statistics.1.content.9.graph.2=usbhdd_used
web.statistics.1.content.9.ds_graph_options.usbhdd_total.label=USB HDD total space (MB)
web.statistics.1.content.9.ds_graph_options.usbhdd_total.color="#FF7777"
web.statistics.1.content.9.ds_graph_options.usbhdd_used.label=USB HDD used space (MB)
web.statistics.1.content.9.ds_graph_options.usbhdd_used.lines={ fill: true }
web.statistics.1.content.9.ds_graph_options.usbhdd_used.color="#7777FF"
_EOF_
			fi

		fi

		software_id=65 # Netdata
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Only required for our self-compiled package, leave defaults for Debian APT package otherwise
			if (( $G_HW_ARCH == 1 && $G_DISTRO < 5 )); then

				# Service
				dps_index=$software_id Download_Install 'netdata.service' /etc/systemd/system/netdata.service

				# Dir (failsafe)
				mkdir -p /var/lib/netdata

				# User
				Create_User -d /var/lib/netdata netdata

				# Permissions: https://docs.netdata.cloud/docs/netdata-security/#netdata-directories
				# - R/O
				chown -R root:netdata /etc/netdata /usr/lib/netdata /usr/libexec/netdata /usr/share/netdata
				chmod -R 0750 /etc/netdata /usr/lib/netdata /usr/libexec/netdata /usr/share/netdata
				# - apps.plugin requires root privileges to read disk I/O
				chmod 4750 /usr/libexec/netdata/plugins.d/apps.plugin
				# - R/W (web access: https://github.com/MichaIng/DietPi/issues/2336#issuecomment-450196178)
				chown -R netdata:netdata /var/cache/netdata /var/lib/netdata /var/log/netdata /usr/share/netdata/web
				chmod -R 0770 /var/cache/netdata /var/lib/netdata /var/log/netdata

			fi

		fi

		software_id=57 # Baïkal
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Enable required PHP modules: https://github.com/sabre-io/Baikal/wiki/Baïkal-dependencies
			G_EXEC phpenmod xml mbstring pdo_mysql

			# Database
			/boot/dietpi/func/create_mysql_db baikal baikal "$GLOBAL_PW"

			# Web server configs: http://sabre.io/baikal/install/ + https://github.com/bambocher/docker-baikal/blob/master/lighttpd.conf
			# - Apache
			if (( ${aSOFTWARE_INSTALL_STATE[83]} > 0 )); then

				dps_index=$software_id Download_Install 'apache.baikal.conf' /etc/apache2/sites-available/dietpi-baikal.conf
				a2ensite dietpi-baikal
				echo '# Redirect Cal/CardDAV requests to Baïkal endpoint:
Redirect permanent /.well-known/carddav /baikal/html/dav.php
Redirect permanent /.well-known/caldav /baikal/html/dav.php' > /etc/apache2/conf-available/dietpi-dav_redirect.conf
				a2enconf dietpi-dav_redirect

			# - Lighttpd
			elif (( ${aSOFTWARE_INSTALL_STATE[84]} > 0 )); then

				dps_index=$software_id Download_Install 'lighttpd.baikal.conf' /etc/lighttpd/conf-available/99-dietpi-baikal.conf
				echo '# Redirect Cal/CardDAV requests to Baïkal endpoint:
url.redirect += (
	"^/.well-known/caldav"  => "/baikal/html/dav.php",
	"^/.well-known/carddav" => "/baikal/html/dav.php"
)' > /etc/lighttpd/conf-available/99-dietpi-dav_redirect.conf
				G_EXEC_POST_FUNC(){ [[ $exit_code == 2 ]] && exit_code=0; } # Do not fail if modules are enabled already
				G_EXEC lighty-enable-mod dietpi-baikal dietpi-dav_redirect

			# - Nginx
			elif (( ${aSOFTWARE_INSTALL_STATE[85]} > 0 )); then

				dps_index=$software_id Download_Install 'nginx.baikal.conf' /etc/nginx/sites-dietpi/dietpi-baikal.conf
				# shellcheck disable=SC2016
				echo '# Redirect Cal/CardDAV requests to Baïkal endpoint:
location = /.well-known/carddav {
	return 301 $scheme://$host/baikal/html/dav.php;
}
location = /.well-known/caldav {
	return 301 $scheme://$host/baikal/html/dav.php;
}' > /etc/nginx/sites-dietpi/dietpi-dav_redirect.conf

			fi

			# Permissions
			G_EXEC chown -R www-data:root /var/www/baikal/{Specific,config}
			G_EXEC find /var/www/baikal/{Specific,config} \( -name '.ht*' -o -name '.git*' \) -exec chown root {} +

		fi

		software_id=43 # Mumble Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Cap total connections
			local max_users=$(( $G_HW_CPU_CORES * 8 ))
			sed -i "/users=/c\users=$max_users" /etc/mumble-server.ini

			# Name the root channel
			sed -i '/registerName=/c\registerName=DietPi Mumble Server' /etc/mumble-server.ini

			# Disable DB logging
			sed -i '/logdays=/c\logdays=-1' /etc/mumble-server.ini

			# Set Superuser passwd: https://dietpi.com/phpbb/viewtopic.php?f=11&t=2024#p8084
			murmurd -ini /etc/mumble-server.ini -supw "$GLOBAL_PW"

		fi

		software_id=41 # Emby Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# User: The DEB package install overrides this, hence the method needs to be changed when using an APT repository!
			Create_User -g dietpi -G emby,video,render -d /var/lib/emby emby

			Download_Test_Media

		fi

		software_id=42 # Plex Media Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# User: Run service as "dietpi" group: https://github.com/MichaIng/DietPi/issues/350#issuecomment-423763518
			Create_User -g dietpi -G plex,video,render -d /var/lib/plexmediaserver plex
			# - Unset explicit group in service file (applied by DEB package) as this may override supplementary group permissions
			G_EXEC mkdir -p /etc/systemd/system/plexmediaserver.service.d
			echo -e '[Service]\nGroup=' > /etc/systemd/system/plexmediaserver.service.d/dietpi-group.conf

			Download_Test_Media

		fi

		software_id=52 # Cuberite
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# User
			Create_User -d /mnt/dietpi_userdata/cuberite cuberite

			# Service
			cat << '_EOF_' > /etc/systemd/system/cuberite.service
[Unit]
Description=Cuberite Server (DietPi)

[Service]
Type=forking
User=cuberite
WorkingDirectory=/mnt/dietpi_userdata/cuberite
ExecStart=/mnt/dietpi_userdata/cuberite/Cuberite --service

[Install]
WantedBy=multi-user.target
_EOF_
			# Web UI settings: Do not overwrite existing!
			[[ -f '/mnt/dietpi_userdata/cuberite/webadmin.ini' ]] || cat << _EOF_ > /mnt/dietpi_userdata/cuberite/webadmin.ini
[User:admin]
Password=$GLOBAL_PW

[WebAdmin]
Ports=1339
Enabled=1
_EOF_
			# Permissions
			G_EXEC chmod +x /mnt/dietpi_userdata/cuberite/Cuberite
			G_EXEC chown -R cuberite:cuberite /mnt/dietpi_userdata/cuberite

		fi

		software_id=53 # MineOS
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 ))
		then
			Banner_Configuration

			# Config: Preserve existing
			[[ -f '/etc/mineos.conf' ]] || G_EXEC cp /mnt/dietpi_userdata/mineos/minecraft/mineos.conf /etc/mineos.conf

			# Create symlinks for console and userdata dir
			G_EXEC ln -sf /mnt/dietpi_userdata/mineos/minecraft/mineos_console.js /usr/local/bin/mineos
			G_EXEC rm -Rf /var/games/minecraft
			G_EXEC mkdir -p /var/games /mnt/dietpi_userdata/mineos/serverdata
			G_EXEC ln -s /mnt/dietpi_userdata/mineos/serverdata /var/games/minecraft

			# Setup SSL cert
			G_EXEC cd /mnt/dietpi_userdata/mineos/minecraft
			G_EXEC_OUTPUT=1 G_EXEC ./generate-sslcert.sh
			G_EXEC cd /tmp/$G_PROGRAM_NAME

			# Service
			cat << _EOF_ > /etc/systemd/system/mineos.service
[Unit]
Description=MineOS (DietPi)
Wants=network-online.target
After=network-online.target dietpi-boot.service

[Service]
WorkingDirectory=/mnt/dietpi_userdata/mineos/minecraft
Environment="SHELL=/bin/bash" "HOME=/root"
SyslogIdentifier=MineOS
ExecStart=$(command -v node) webui.js
KillMode=process

[Install]
WantedBy=multi-user.target
_EOF_
		fi

		software_id=49 # Gogs
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# User
			Create_User -d /etc/gogs gogs

			# Directories + permissions
			G_EXEC mkdir -p /mnt/dietpi_userdata/gogs-repo /var/log/gogs
			G_EXEC chown -R gogs:gogs /etc/gogs /mnt/dietpi_userdata/gogs-repo /var/log/gogs

			# Database
			if [[ ! -d '/var/lib/mysql/gogs' ]]; then

				G_EXEC systemctl restart mariadb
				mysql -f < /etc/gogs/scripts/mysql.sql # -f until: https://github.com/gogs/gogs/pull/6424
				mysql -e "grant all privileges on gogs.* to gogs@localhost identified by '$GLOBAL_PW'"

			fi

			# Service: https://github.com/gogs/gogs/blob/main/scripts/systemd/gogs.service
			cat << '_EOF_' > /etc/systemd/system/gogs.service
[Unit]
Description=Gogs (DietPi)
Wants=network-online.target
After=network-online.target dietpi-boot.service mariadb.service

[Service]
User=gogs
WorkingDirectory=/etc/gogs
ExecStart=/etc/gogs/gogs web

# Hardening
ProtectSystem=full
PrivateDevices=yes
PrivateTmp=yes
NoNewPrivileges=true
ReadWritePaths=/etc/gogs

[Install]
WantedBy=multi-user.target
_EOF_
		fi

		software_id=46 # qBittorrent
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# User
			Create_User -g dietpi -d /home/qbittorrent -p "$GLOBAL_PW" qbittorrent

			# Config: Create only on fresh install
			if [[ ! -f '/home/qbittorrent/.config/qBittorrent/qBittorrent.conf' ]]; then

				G_EXEC mkdir -p /home/qbittorrent/.config/qBittorrent /var/log/qbittorrent
				G_EXEC chown -R qbittorrent: /var/log/qbittorrent
				cat << _EOF_ > /home/qbittorrent/.config/qBittorrent/qBittorrent.conf
[General]
ported_to_new_savepath_system=true

[Application]
FileLogger\Enabled=true
FileLogger\Path=/var/log/qbittorrent

[Preferences]
Downloads\DiskWriteCacheSize=$(Optimise_BitTorrent 0)
Downloads\DiskWriteCacheTTL=60
Queueing\MaxActiveDownloads=$(Optimise_BitTorrent 1)
Queueing\MaxActiveTorrents=$(Optimise_BitTorrent 1)
Queueing\MaxActiveUploads=1
Queueing\IgnoreSlowTorrents=false
Bittorrent\MaxConnecs=$(Optimise_BitTorrent 2)
Bittorrent\MaxConnecsPerTorrent=$(Optimise_BitTorrent 2)
Bittorrent\MaxUploads=$(Optimise_BitTorrent 3)
Bittorrent\MaxUploadsPerTorrent=$(Optimise_BitTorrent 3)
WebUI\Port=1340
WebUI\Enabled=true
General\Locale=en
Downloads\SavePath=/mnt/dietpi_userdata/downloads
Downloads\TempPathEnabled=false
Downloads\TempPath=/mnt/dietpi_userdata/downloads
Downloads\ScanDirs=@Invalid()
Downloads\DownloadInScanDirs=@Invalid()
Downloads\TorrentExportDir=
MailNotification\enabled=false
MailNotification\email=
MailNotification\smtp_server=smtp.changeme.com
MailNotification\req_ssl=false
MailNotification\req_auth=false
MailNotification\username=
MailNotification\password=
Downloads\PreAllocation=false
Queueing\QueueingEnabled=false
Downloads\UseIncompleteExtension=false
Connection\PortRangeMin=6881
Connection\UPnP=true
Connection\GlobalDLLimit=-1
Connection\GlobalUPLimit=-1
Bittorrent\uTP=true
Bittorrent\uTP_rate_limited=false
Advanced\IncludeOverhead=false
Connection\GlobalDLLimitAlt=10
Connection\GlobalUPLimitAlt=10
Scheduler\Enabled=false
Bittorrent\DHT=true
Bittorrent\sameDHTPortAsBT=true
Bittorrent\DHTPort=6881
Bittorrent\PeX=true
Bittorrent\LSD=true
Bittorrent\Encryption=1
Advanced\AnonymousMode=false
Connection\ProxyType=-1
Connection\Proxy\IP=0.0.0.0
Connection\Proxy\Port=8080
Connection\ProxyPeerConnections=false
Connection\Proxy\Authentication=false
Connection\Proxy\Username=
Connection\Proxy\Password=
IPFilter\Enabled=false
IPFilter\File=
WebUI\Username=qbittorrent
WebUI\LocalHostAuth=true
WebUI\HTTPS\Enabled=false
DynDNS\Enabled=false
DynDNS\Service=0
DynDNS\Username=
DynDNS\Password=
DynDNS\DomainName=changeme.dyndns.org
WebUI\Password_ha1=@ByteArray($(echo -n "$GLOBAL_PW" | md5sum | mawk '{print $1}'))

[LegalNotice]
Accepted=true

[AutoRun]
enabled=false
program=
_EOF_
			fi

			# Service
			cat << _EOF_ > /etc/systemd/system/qbittorrent.service
[Unit]
Description=qBittorrent (DietPi)
Documentation=man:qbittorrent-nox(1)
Wants=network-online.target
After=network-online.target dietpi-boot.service

[Service]
User=qbittorrent
UMask=002
ExecStart=$(command -v qbittorrent-nox)

[Install]
WantedBy=multi-user.target
_EOF_
			# Permissions
			G_EXEC chown -R qbittorrent: /home/qbittorrent

		fi

		software_id=107 # rTorrent
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Create username/password for rutorrent based on webserver type
			# - Apache2
			if (( ${aSOFTWARE_INSTALL_STATE[83]} > 0 )); then

				# Install SCGI module
				G_AGI libapache2-mod-scgi
				a2enmod rewrite scgi

				# Enable password protection
				htpasswd -cb /etc/.rutorrent-htaccess root "$GLOBAL_PW"
				cat << _EOF_ > /var/www/rutorrent/.htaccess
AuthUserFile /etc/.rutorrent-htaccess
AuthName "ruTorrent_login"
AuthType Basic
require user root
_EOF_

				# Allow overrides/.htaccess and enable SCGI + authentication
				cat << _EOF_ > /etc/apache2/sites-available/dietpi-rutorrent.conf
<Directory /var/www/rutorrent/>
	AllowOverride All
</Directory>

SCGIMount /RPC2 127.0.0.1:5000
<location /RPC2>
	AuthName "rTorrent secure access"
	AuthType Basic
	AuthBasicProvider file
	AuthUserFile /etc/.rutorrent-htaccess
	Require user root
</location>
_EOF_
				a2ensite dietpi-rutorrent

			# - Lighttpd
			elif (( ${aSOFTWARE_INSTALL_STATE[84]} > 0 )); then

				echo "root:rtorrent:$(echo -n 'root:rtorrent:dietpi' | md5sum | cut -b -32)" > /etc/.rutorrent-htaccess

				# Add to /etc/lighttpd/lighttpd.conf
				if ! grep -q '^#RUTORRENT_DIETPI' /etc/lighttpd/lighttpd.conf; then

					cat << '_EOF_' >> /etc/lighttpd/lighttpd.conf
#RUTORRENT_DIETPI
server.modules += ( "mod_fastcgi" )
server.modules += ( "mod_scgi" )
server.modules += ( "mod_auth" )
auth.debug = 0
auth.backend = "htdigest"
auth.backend.htdigest.userfile = "/etc/.rutorrent-htaccess"

auth.require = ( "/rutorrent/" => (
                    "method"  => "digest",
                    "realm"   => "rtorrent",
                    "require" => "valid-user"
               ))

scgi.server = ( "/RPC2" =>
    ( "127.0.0.1" =>
        (
            "host" => "127.0.0.1",
            "port" => 5000,
            "check-local" => "disable"
        )
    )
)
#RUTORRENT_DIETPI
_EOF_

				fi

			# - Nginx
			elif (( ${aSOFTWARE_INSTALL_STATE[85]} > 0 )); then

				echo "root:$(openssl passwd -crypt dietpi)" > /etc/.rutorrent-htaccess

				cat << _EOF_ > /etc/nginx/sites-dietpi/dietpi-rutorrent.conf
location /rutorrent {
    auth_basic "Restricted Content";
    auth_basic_user_file /etc/.rutorrent-htaccess;
}

location /RPC2 {
    include scgi_params;
    scgi_pass 127.0.0.1:5000;
}
_EOF_

			fi

			# Define curl location in config.php (for Lighttpd and Nginx)
			G_CONFIG_INJECT '"curl"[[:blank:]]' '		"curl"	=> "/usr/bin/curl",' /var/www/rutorrent/conf/config.php

			chown www-data:www-data /etc/.rutorrent-htaccess
			chmod 400 /etc/.rutorrent-htaccess

			# Session folder
			mkdir -p /mnt/dietpi_userdata/downloads/.session

			# Create rTorrent run user
			Create_User -g dietpi -d /mnt/dietpi_userdata/rtorrent rtorrent

			# Service using screen | '/usr/bin/rtorrent &> /var/log/rtorrent.log &' doesnt work, hangs program after 5 seconds
			cat << _EOF_ > /etc/systemd/system/rtorrent.service
[Unit]
Description=rTorrent (DietPi)
Wants=network-online.target
After=network-online.target

[Service]
Type=forking
User=rtorrent
KillMode=none
ExecStart=$(command -v screen) -fa -dmS rtorrent $(command -v rtorrent)
ExecStop=$(command -v screen) -S rtorrent -X quit

[Install]
WantedBy=multi-user.target
_EOF_
			# On Buster we use new daemon mode
			if (( $G_DISTRO > 4 )); then

				sed -Ei '/^(Type|KillMode|ExecStop)=/d' /etc/systemd/system/rtorrent.service
				G_CONFIG_INJECT 'ExecStart=' "ExecStart=$(command -v rtorrent)" /etc/systemd/system/rtorrent.service

			fi

			# Default conf, do not overwrite if existent
			# - Example: https://github.com/rakshasa/rtorrent/blob/master/doc/rtorrent.rc
			# - Deprecated commands:
			#	https://github.com/rakshasa/rtorrent/wiki/rTorrent-0.9-Comprehensive-Command-list-(WIP)
			#	https://github.com/rakshasa/rtorrent/blob/master/doc/scripts/update_commands_0.9.sed
			G_EXEC mkdir -p /mnt/dietpi_userdata/rtorrent
			[[ -f '/mnt/dietpi_userdata/rtorrent/.rtorrent.rc' ]] || cat << _EOF_ > /mnt/dietpi_userdata/rtorrent/.rtorrent.rc
### Miscellaneous settings
#system.daemon.set = true
# Default download dir
directory.default.set = /mnt/dietpi_userdata/downloads
# Session dir
session.path.set = /mnt/dietpi_userdata/downloads/.session
# Save session every 5 minutes
schedule2 = session_save, 240, 300, ((session.save))
# Close torrents on low diskspace, check every minute
schedule2 = monitor_diskspace, 15, 60, ((close_low_diskspace, 1000M))
system.umask.set = 002
# Max memory mapping size, not max physical RAM usage!
pieces.memory.max.set = ${RAM_PHYS}M
pieces.hash.on_completion.set = no

### Connection settings
# Incoming connection ports
network.port_open.set = no
network.port_random.set = no
#network.port_range.set = 33101-33199
# SCGI connection, e.g. for ruTorrent web UI
network.scgi.open_port = 127.0.0.1:5000
#network.scgi.open_local = /mnt/dietpi_userdata/downloads/.session/rpc.socket
#execute.nothrow = chmod,777,/mnt/dietpi_userdata/downloads/.session/rpc.socket

### Network limits
network.http.max_open.set = $(Optimise_BitTorrent 2)
network.max_open_files.set = $(( $(Optimise_BitTorrent 2) * 2 ))
network.max_open_sockets.set = $(Optimise_BitTorrent 2)
# Global max download/upload rate in KiB, "0" for unlimited
throttle.global_down.max_rate.set_kb = 0
throttle.global_up.max_rate.set_kb = 0

### Peer settings
# Max downloads/uploads accross all torrents
throttle.max_downloads.global.set = $(Optimise_BitTorrent 2)
throttle.max_uploads.global.set = $(Optimise_BitTorrent 2)
# Max downloads/uploads per torrent
throttle.max_downloads.set = $(( $(Optimise_BitTorrent 2) / 2 ))
throttle.max_uploads.set = $(( $(Optimise_BitTorrent 2) / 2 ))
# Min/Max connected peers
throttle.min_peers.normal.set = $(( $(Optimise_BitTorrent 2) - 1))
throttle.max_peers.normal.set = $(Optimise_BitTorrent 2)
throttle.min_peers.seed.set = -1
throttle.max_peers.seed.set = -1
trackers.numwant.set = $(Optimise_BitTorrent 2)
# Public tracker support
trackers.use_udp.set = yes
dht.mode.set = on
#dht.port.set = 6881
protocol.pex.set = yes
protocol.encryption.set = allow_incoming,try_outgoing,enable_retry
_EOF_
			# On Buster use new daemon mode
			(( $G_DISTRO > 4 )) && G_CONFIG_INJECT 'system.daemon.set[[:blank:]=]' 'system.daemon.set = true' /mnt/dietpi_userdata/rtorrent/.rtorrent.rc

			# Permissions
			chown -R rtorrent:dietpi /mnt/dietpi_userdata/rtorrent /mnt/dietpi_userdata/downloads/.session
			# - ruTorrent: https://github.com/Novik/ruTorrent/wiki/Config
			chown -R www-data:dietpi /var/www/rutorrent/share
			chown root:root /var/www/rutorrent/share/.htaccess

		fi

		software_id=132 # Aria2
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# User
			Create_User -g dietpi -d /mnt/dietpi_userdata/aria2 aria2

			# Config
			mkdir -p /mnt/dietpi_userdata/aria2
			[[ -f '/mnt/dietpi_userdata/aria2/aria2.conf' ]] || cat << _EOF_ > /mnt/dietpi_userdata/aria2/aria2.conf
# DietPi default aria2c options served as aria2.conf file via --conf-path
# https://aria2.github.io/manual/en/html/aria2c.html#options

# Logging
log=/var/log/aria2.log
log-level=warn

# Session
always-resume=true
continue=true
input-file=/mnt/dietpi_userdata/downloads/aria2.session
save-session=/mnt/dietpi_userdata/downloads/aria2.session
save-session-interval=60

# Connection
listen-port=6881-6999
ftp-pasv=true
check-certificate=false
# - Count
max-concurrent-downloads=$(Optimise_BitTorrent 1)
split=$(Optimise_BitTorrent 1)
max-connection-per-server=$(Optimise_BitTorrent 1)
bt-max-peers=$(Optimise_BitTorrent 2)
# - Bandwidth
max-overall-upload-limit=0
max-upload-limit=0
max-overall-download-limit=0
max-download-limit=0
# - Retries
max-file-not-found=3
max-tries=5
retry-wait=60

# RPC
enable-rpc=true
rpc-listen-all=true
rpc-listen-port=6800
rpc-secret=$GLOBAL_PW
rpc-allow-origin-all=true
pause=false

# Store
dir=/mnt/dietpi_userdata/downloads
allow-overwrite=false
auto-file-renaming=false
file-allocation=none
check-integrity=true

# Seeding
seed-ratio=0.1
seed-time=0
_EOF_

			# Pre-create log file
			local fp_log=$(sed -n '/^[[:blank:]]*log=/{s/^[^=]*=//p;q}' /mnt/dietpi_userdata/aria2/aria2.conf)
			[[ ${fp_log//\"} && ! -f $fp_log ]] && >> "$fp_log"

			# Pre-create input file
			local fp_input=$(sed -n '/^[[:blank:]]*input-file=/{s/^[^=]*=//p;q}' /mnt/dietpi_userdata/aria2/aria2.conf)
			[[ ${fp_input//\"} && ! -f $fp_input ]] && >> "$fp_input"

			# Permissions
			chown -R aria2:root /mnt/dietpi_userdata/aria2 "$fp_input"
			chown aria2:dietpi "$fp_log"
			chmod 600 /mnt/dietpi_userdata/aria2/aria2.conf
			unset -v fp_log fp_input

			# Service
			cat << _EOF_ > /etc/systemd/system/aria2.service
[Unit]
Description=Aria2 (DietPi)

[Service]
User=aria2
UMask=002
ExecStart=$(command -v aria2c) --conf-path=/mnt/dietpi_userdata/aria2/aria2.conf

[Install]
WantedBy=multi-user.target
_EOF_

		fi

		software_id=50 # Syncthing
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Permissions
			chown -R dietpi:dietpi /opt/syncthing

			# Pre-create and edit default config only on fresh installs
			if [[ ! -f '/mnt/dietpi_userdata/syncthing/config.xml' ]]; then

				# Run Syncthing to pre-create config dir and exit
				sudo -u dietpi /opt/syncthing/syncthing -generate=/mnt/dietpi_userdata/syncthing

				# Allow remote access: https://docs.syncthing.net/users/faq.html#how-do-i-access-the-web-gui-from-another-computer
				sed -i '\|:8384</address>|c\        <address>0.0.0.0:8384</address>' /mnt/dietpi_userdata/syncthing/config.xml

				# Set default folder
				mkdir -p /mnt/dietpi_userdata/syncthing_data
				chown -R dietpi:dietpi /mnt/dietpi_userdata/syncthing_data
				sed -i '\|<folder id="default"|s|label="[^"]*"|label="Syncthing Data"|' /mnt/dietpi_userdata/syncthing/config.xml
				sed -i '\|<folder id="default"|s|path="[^"]*"|path="/mnt/dietpi_userdata/syncthing_data"|' /mnt/dietpi_userdata/syncthing/config.xml

			fi

			# Service: https://github.com/syncthing/syncthing/blob/master/etc/linux-systemd/system/syncthing%40.service
			cat << '_EOF_' > /etc/systemd/system/syncthing.service
[Unit]
Description=Syncthing (DietPi)
Wants=network-online.target
After=network-online.target

[Service]
User=dietpi
ExecStart=/opt/syncthing/syncthing -no-browser -no-restart -logflags=0 -home=/mnt/dietpi_userdata/syncthing
Restart=on-failure
SuccessExitStatus=3 4
RestartForceExitStatus=3 4

# Hardening
ProtectSystem=full
PrivateTmp=true
SystemCallArchitectures=native
MemoryDenyWriteExecute=true
NoNewPrivileges=true

[Install]
WantedBy=multi-user.target
_EOF_
			# Increase fs watcher limit: https://docs.syncthing.net/users/faq.html#how-do-i-increase-the-inotify-limit-to-get-my-filesystem-watcher-to-work
			echo 'fs.inotify.max_user_watches=204800' > /etc/sysctl.d/dietpi-syncthing.conf

		fi

		software_id=111 # UrBackup Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# As we have /tmp mounted to RAM, change tmp location
			mkdir -p /mnt/dietpi_userdata/urbackup/tmp
			chown -R urbackup:urbackup /mnt/dietpi_userdata/urbackup
			G_CONFIG_INJECT 'DAEMON_TMPDIR=' "DAEMON_TMPDIR='/mnt/dietpi_userdata/urbackup/tmp'" /etc/default/urbackupsrv

			# https://github.com/MichaIng/DietPi/issues/545#issuecomment-252419739
			#sqlite3 /usr/local/var/urbackup/backup_server_settings.db "UPDATE settings SET value = '/mnt/dietpi_userdata/urbackup/' WHERE key = 'backupfolder'"

		fi

		software_id=116 # Medusa
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# User
			Create_User -g dietpi -d /mnt/dietpi_userdata/medusa medusa

			# Service: https://github.com/pymedusa/Medusa/blob/master/runscripts/init.systemd
			G_EXEC cp /mnt/dietpi_userdata/medusa/runscripts/init.systemd /etc/systemd/system/medusa.service
			# - Prevent unlimited restarts on failure: Permit up to three restarts in 10 minutes
			G_CONFIG_INJECT 'StartLimitIntervalSec=' 'StartLimitIntervalSec=600' /etc/systemd/system/medusa.service '\[Unit\]'
			G_CONFIG_INJECT 'StartLimitBurst=' 'StartLimitBurst=3' /etc/systemd/system/medusa.service 'StartLimitIntervalSec='
			# - Remove "Group=medusa" which does not exist, instead fallback to prinmary group "dietpi"
			G_EXEC sed -i '/^[[:blank:]]*Group=/d' /etc/systemd/system/medusa.service
			# - Launch from our install and data directory
			G_CONFIG_INJECT 'ExecStart=' "ExecStart=$(command -v python3) /mnt/dietpi_userdata/medusa/start.py -q --nolaunch --datadir=/mnt/dietpi_userdata/medusa" /etc/systemd/system/medusa.service

			# Permissions
			G_EXEC chown -R medusa:dietpi /mnt/dietpi_userdata/medusa

		fi

		software_id=113 # Chromium
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Allow root, start maximized and disable sandbox under root (blank screen without)
			local export_options="export CHROMIUM_FLAGS=\"\$CHROMIUM_FLAGS \
--test-type \
--no-sandbox \
--temp-profile \
--user-data-dir \
--disable-smooth-scrolling \
--disable-low-res-tiling \
--enable-low-end-device-mode \
--num-raster-threads=$G_HW_CPU_CORES \
--profiler-timing=0 \
--disable-composited-antialiasing "

			# RPi: Leave OpenGL choice to user, as it also depends on RPi model
			if (( $G_HW_MODEL < 10 )); then

				:

			# x86_64: OpenGL
			elif [[ $G_HW_MODEL == 2[01] ]]; then

				:

			# GLES
			else

				export_options+='--use-gl=egl'

			fi

			export_options+='"'

			G_EXEC mkdir -p /etc/chromium.d
			echo "$export_options" > /etc/chromium.d/custom_flags

			# Chromium 60+
			G_EXEC cp /etc/chromium.d/custom_flags /root/.chromium-browser.init

			# Desktop shortcut: chromium-browser.desktop on RPi
			if (( $G_HW_MODEL > 9 ))
			then
				Create_Desktop_Shortcut chromium
			else
				Create_Desktop_Shortcut chromium-browser
			fi

			# Autostart run script for Kiosk mode, based on @AYapejian https://github.com/MichaIng/DietPi/issues/1737#issue-318697621
			cat << '_EOF_' > /var/lib/dietpi/dietpi-software/installed/chromium-autostart.sh
#!/bin/bash
# Autostart run script for Kiosk mode, based on @AYapejian https://github.com/MichaIng/DietPi/issues/1737#issue-318697621
# - Please see /root/.chromium-browser.init (and /etc/chromium.d/custom_flags) for additional egl/gl init options

# Command line switches https://peter.sh/experiments/chromium-command-line-switches/
# --test-type gets rid of some of the chromium warnings that you may or may not care about in kiosk on a LAN
# --pull-to-refresh=1
# --ash-host-window-bounds="400,300"

# Resolution to use for kiosk mode, should ideally match current system resolution
RES_X=$(sed -n '/^[[:blank:]]*SOFTWARE_CHROMIUM_RES_X=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
RES_Y=$(sed -n '/^[[:blank:]]*SOFTWARE_CHROMIUM_RES_Y=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)

CHROMIUM_OPTS="--kiosk --test-type --window-size=$RES_X,$RES_Y --start-fullscreen --start-maximized --window-position=0,0"
# If you want tablet mode, uncomment the next line.
#CHROMIUM_OPTS+=' --force-tablet-mode --tablet-ui'

# Add URL for first run:
URL=$(sed -n '/^[[:blank:]]*SOFTWARE_CHROMIUM_AUTOSTART_URL=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
CHROMIUM_OPTS+=" --homepage $URL"

# Find absolute filepath location of Chromium binary.
FP_CHROMIUM=$(command -v chromium)
if [[ ! $FP_CHROMIUM ]]; then

	# Assume RPi
	FP_CHROMIUM="$(command -v chromium-browser)"

fi

xinit $FP_CHROMIUM $CHROMIUM_OPTS
_EOF_
			G_EXEC chmod +x /var/lib/dietpi/dietpi-software/installed/chromium-autostart.sh

		fi

		software_id=129 # O!MPD
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Preserve config file, create local override if non-existent
			# Database will be migrated automatically, so only create new on fresh install, using local override config as flag
			if [[ ! -f '/var/www/ompd/include/config.local.inc.php' ]]; then

				G_EXEC systemctl start mariadb
				# - Create database user only, database will be created automatically
				mysql -e "grant all privileges on ompd.* to ompd@localhost identified by '$GLOBAL_PW'"
				systemctl stop mariadb

				cat << _EOF_ > /var/www/ompd/include/config.local.inc.php
<?php
# Database
\$cfg['mysqli_host']                 = '127.0.0.1';
\$cfg['mysqli_db']                   = 'ompd';
\$cfg['mysqli_user']                 = 'ompd';
\$cfg['mysqli_password']             = '$GLOBAL_PW';
\$cfg['mysqli_port']                 = '';
\$cfg['mysqli_socket']               = '/run/mysqld/mysqld.sock';
\$cfg['mysqli_auto_create_db']       = true;
# Media dir
\$cfg['media_dir']                   = '/mnt/dietpi_userdata/Music';
?>
_EOF_

			fi

			# Webserver configs
			# - Lighttpd
			if (( ${aSOFTWARE_INSTALL_STATE[84]} > 0 )); then

				dps_index=$software_id Download_Install 'lighttpd.ompd.conf' /etc/lighttpd/conf-available/99-dietpi-ompd.conf
				G_EXEC_POST_FUNC(){ [[ $exit_code == 2 ]] && exit_code=0; } # Do not fail if modules are enabled already
				G_EXEC lighty-enable-mod dietpi-ompd

			fi

			# Permissions: http://ompd.pl/configuration
			chown -R www-data:www-data /var/www/ompd/{tmp,stream,cache}

		fi

		software_id=135 # IceCast + DarkIce
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# IceCast set passwords
			sed -i '/<source-password>/c\        <source-password>'"$GLOBAL_PW"'</source-password>' /etc/icecast2/icecast.xml
			sed -i '/<relay-password>/c\        <relay-password>'"$GLOBAL_PW"'</relay-password>' /etc/icecast2/icecast.xml

			# - Create random admin password
			local admin_password=$(tr -cd '[:alnum:]' < /dev/urandom | fold -w10 | head -1)
			sed -i "/<admin-password>/c\        <admin-password>$admin_password</admin-password>" /etc/icecast2/icecast.xml

			# DarkIce
			local input_device_index=$(arecord -l | mawk -F'[ :]' '/card/{print $2;exit}')

			cat << _EOF_ > /etc/darkice.cfg
[general]
duration      = 0
bufferSecs    = 3
reconnect     = yes

[input]
device        = hw:$input_device_index,0
sampleRate    = 44100
bitsPerSample = 16
channel       = 1

[icecast2-0]
bitrateMode   = vbr
format        = vorbis
quality       = 0.8
server        = dietpi
port          = 8000
password      = $GLOBAL_PW
mountPoint    = DietPi
name          = DietPi
description   = DarkIce on DietPi
url           = http://localhost
genre         = none
public        = no
#localDumpFile = /mnt/dietpi_userdata/darkice_recording.ogg
_EOF_
			# systemd service for DarkIce
			[[ -f '/etc/init.d/darkice' ]] && rm /etc/init.d/darkice
			update-rc.d -f darkice remove
			[[ -f '/etc/default/icecast2' ]] && rm /etc/default/icecast2
			cat << _EOF_ > /etc/systemd/system/darkice.service
[Unit]
Description=DarkIce (DietPi)
Requires=icecast2.service
After=icecast2.service

[Service]
ExecStart=$(command -v darkice)

[Install]
WantedBy=multi-user.target
_EOF_
		fi

		software_id=123 # Mosquitto
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Remove obsolete sysvinit service
			[[ -f '/etc/init.d/mosquitto' ]] && rm -v /etc/init.d/mosquitto
			update-rc.d -f mosquitto remove

			# Add systemd unit if missing, since Buster and with official repo it is part of the DEB package: https://github.com/eclipse/mosquitto/tree/master/service/systemd
			[[ -f '/lib/systemd/system/mosquitto.service' ]] || cat << '_EOF_' > /etc/systemd/system/mosquitto.service
[Unit]
Description=Mosquitto MQTT Broker (DietPi)
Documentation=man:mosquitto.conf(5) man:mosquitto(8)
Wants=network-online.target
After=network-online.target dietpi-boot.service

[Service]
Type=notify
NotifyAccess=main
ExecStartPre=/bin/mkdir -m 740 -p /var/log/mosquitto
ExecStartPre=/bin/chown mosquitto: /var/log/mosquitto
ExecStart=/usr/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure

[Install]
WantedBy=multi-user.target
_EOF_
		fi

		software_id=131 # Blynk Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Preserve existing config
			if [[ ! -f '/mnt/dietpi_userdata/blynk/server.properties' ]]; then

				Download_Install 'https://raw.githubusercontent.com/blynkkk/blynk-server/master/server/core/src/main/resources/server.properties' /mnt/dietpi_userdata/blynk/server.properties
				G_CONFIG_INJECT 'data.folder=' 'data.folder=/mnt/dietpi_userdata/blynk/data' /mnt/dietpi_userdata/blynk/server.properties
				# Log to RAMlog
				G_EXEC mkdir -p /var/log/blynk
				G_CONFIG_INJECT 'logs.folder=' 'logs.folder=/var/log/blynk' /mnt/dietpi_userdata/blynk/server.properties

			fi

			# User
			Create_User -g dietpi -d /mnt/dietpi_userdata/blynk blynk

			# Service
			cat << _EOF_ > /etc/systemd/system/blynkserver.service
[Unit]
Description=Blynk Server (DietPi)
Wants=network-online.target
After=network-online.target dietpi-boot.service

[Service]
User=blynk
WorkingDirectory=/mnt/dietpi_userdata/blynk
ExecStart=$(command -v java) -jar /mnt/dietpi_userdata/blynk/blynkserver.jar

[Install]
WantedBy=multi-user.target
_EOF_
			# Permissions
			G_EXEC chown -R blynk:dietpi /mnt/dietpi_userdata/blynk /var/log/blynk

		fi

		software_id=136 # MotionEye
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Enable RPi cam
			(( $G_HW_MODEL < 10 )) && /boot/dietpi/func/dietpi-set_hardware rpi-camera enable

			# Config
			mkdir -p /etc/motioneye
			G_BACKUP_FP /etc/motioneye/motioneye.conf
			cp /usr/local/share/motioneye/extra/motioneye.conf.sample /etc/motioneye/motioneye.conf

			# Data
			mkdir -p /mnt/dietpi_userdata/motioneye
			sed -i '/^media_path/c\media_path /mnt/dietpi_userdata/motioneye' /etc/motioneye/motioneye.conf

			# Service
			cp /usr/local/share/motioneye/extra/motioneye.systemd-unit-local /etc/systemd/system/motioneye.service

		fi

		software_id=137 # mjpg-streamer
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 ))
		then
			Banner_Configuration

			# User
			Create_User -g video -d /opt/mjpg-streamer mjpg-streamer

			# Service
			# - On RPi, if the RPi camera module is enabled already, use it by default.
			local input='input_uvc.so -d /dev/video0'
			[[ $G_HW_MODEL -gt 9 || -f '/etc/modprobe.d/dietpi-disable_rpi_camera.conf' ]] || input='input_raspicam.so'
			cat << _EOF_ > /etc/systemd/system/mjpg-streamer.service
[Unit]
Description=mjpg-streamer (DietPi)
Documentation=https://github.com/jacksonliam/mjpg-streamer/tree/master/mjpg-streamer-experimental
Wants=network-online.target
After=network-online.target dietpi-boot.service

[Service]
User=mjpg-streamer
WorkingDirectory=/opt/mjpg-streamer
ExecStart=/opt/mjpg-streamer/mjpg_streamer -i '$input' -o 'output_http.so -p 8082 -n'

# Hardening
ProtectSystem=strict
PrivateTmp=true
ProtectHome=true
ProtectKernelTunables=true
ProtectControlGroups=true

[Install]
WantedBy=multi-user.target
_EOF_
			# OctoPrint: Configure it to use mjpg-streamer, if installed
			if [[ ${aSOFTWARE_INSTALL_STATE[153]} == 2 && -f '/mnt/dietpi_userdata/octoprint/.octoprint/config.yaml' ]]
			then
				G_DIETPI-NOTIFY 2 'Configuring OctoPrint to use mjpg-streamer for webcam support'
				G_EXEC sudo -u octoprint /mnt/dietpi_userdata/octoprint/.local/bin/octoprint config set webcam.stream "http://$(mawk 'NR==4' /run/dietpi/.network):8082/?action=stream"
				G_EXEC sudo -u octoprint /mnt/dietpi_userdata/octoprint/.local/bin/octoprint config set webcam.snapshot 'http://127.0.0.1:8082/?action=snapshot'
				G_EXEC sudo -u octoprint /mnt/dietpi_userdata/octoprint/.local/bin/octoprint config set webcam.ffmpeg "$(command -v ffmpeg)"
			fi
		fi

		software_id=138 # VirtualHere
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Service
			cat << '_EOF_' > /etc/systemd/system/virtualhere.service
[Unit]
Description=VirtualHere (DietPi)

[Service]
ExecStart=/etc/vhusbd/vhusbd -r /var/log/virtualhere.log

[Install]
WantedBy=multi-user.target
_EOF_
			# Config
			echo "ServerName='DietPi'" > /etc/vhusbd/config.ini

		fi

		software_id=139 # SABnzbd
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# User
			Create_User -g dietpi -d /etc/sabnzbd sabnzbd

			# Service: https://github.com/sabnzbd/sabnzbd/blob/master/linux/sabnzbd%40.service
			# - Options: https://sabnzbd.org/wiki/advanced/command-line-parameters
			#	"-OO":	Optimise code and remove doc lines (default shebang of SABnzbd.py, but we run python3 explicitly to avoid version conflicts)
			#	"-d": Run in daemon mode without terminal and browser start (requires "-f </path/to/config.ini>")
			#	      NB: In systemd unit leads to unreliable long taking webserver start. A new process is forked which allows web access, but sometimes after very long time, sometimes never: https://github.com/sabnzbd/sabnzbd/issues/1283
			#	"-b 0":	Do no start browser with daemon
			cat << _EOF_ > /etc/systemd/system/sabnzbd.service
[Unit]
Description=SABnzbd (DietPi)
Documentation=https://sabnzbd.org/wiki/
Wants=network-online.target
After=network-online.target dietpi-boot.service

[Service]
User=sabnzbd
ExecStart=$(command -v python3) -OO /etc/sabnzbd/SABnzbd.py -b 0 -f /etc/sabnzbd/sabnzbd.ini
Restart=on-failure

[Install]
WantedBy=multi-user.target
_EOF_
			# Log dir and permissions
			G_EXEC mkdir -p /var/log/sabnzbd
			G_EXEC chown -R sabnzbd:root /{etc,var/log}/sabnzbd

			# Config
			# - Touch only if it does not yet exist, assume reinstall otherwise and preserve custom changes
			# - API keys and initial config are only generated during 1st run
			# - We need to launch program, then apply our config tweaks, else, wizard setup in web interface simply loops without API keys.
			if [[ ! -f '/etc/sabnzbd/sabnzbd.ini' ]]; then

				Create_Config /etc/sabnzbd/sabnzbd.ini sabnzbd
				sleep 1 # Additional wait, config being overwritten after below changes: https://dietpi.com/phpbb/viewtopic.php?p=7082#p7082

				G_CONFIG_INJECT 'download_dir =' 'download_dir = /mnt/dietpi_userdata/downloads/incomplete' /etc/sabnzbd/sabnzbd.ini
				G_CONFIG_INJECT 'complete_dir =' 'complete_dir = /mnt/dietpi_userdata/downloads/complete' /etc/sabnzbd/sabnzbd.ini
				G_CONFIG_INJECT 'nzb_backup_dir =' 'nzb_backup_dir = /mnt/dietpi_userdata/downloads/sabnzbd_nzb_backup' /etc/sabnzbd/sabnzbd.ini
				G_CONFIG_INJECT 'admin_dir =' 'admin_dir = /mnt/dietpi_userdata/downloads/sabnzbd_admin' /etc/sabnzbd/sabnzbd.ini
				G_CONFIG_INJECT 'log_dir =' 'log_dir = /var/log/sabnzbd' /etc/sabnzbd/sabnzbd.ini
				G_CONFIG_INJECT 'log_level =' 'log_level = 0' /etc/sabnzbd/sabnzbd.ini # Warning errors only
				G_CONFIG_INJECT 'refresh_rate =' 'refresh_rate = 2' /etc/sabnzbd/sabnzbd.ini
				G_CONFIG_INJECT 'host =' 'host = 0.0.0.0' /etc/sabnzbd/sabnzbd.ini
				G_CONFIG_INJECT 'permissions =' 'permissions = 0775' /etc/sabnzbd/sabnzbd.ini
				G_CONFIG_INJECT 'auto_browser =' 'auto_browser = 0' /etc/sabnzbd/sabnzbd.ini

			fi

			# Install language packs: https://github.com/MichaIng/DietPi/issues/1917#issue-340631943
			G_EXEC cd /etc/sabnzbd
			G_EXEC python3 tools/make_mo.py
			G_EXEC cd /tmp/$G_PROGRAM_NAME

		fi

		software_id=177 # Firefox Sync Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# User
			Create_User -d /mnt/dietpi_userdata/firefox-sync ffsync

			# Create database and config directory
			G_EXEC mkdir -p /mnt/dietpi_userdata/firefox-sync

			# Create database if it doesn't exist
			[[ -f '/mnt/dietpi_userdata/firefox-sync/FF-Sync-DB.db' ]] || G_EXEC sqlite3 /mnt/dietpi_userdata/firefox-sync/FF-Sync-DB.db 'create table aTable(field1 int); drop table aTable;'

			# Copy and modify default config file if it doesn't exist
			local ffsync_conf='/mnt/dietpi_userdata/firefox-sync/syncserver.ini'
			if [[ ! -f $ffsync_conf ]]; then

				G_EXEC cp -a /opt/firefox-sync/syncserver.ini $ffsync_conf
				local ffsync_ip=$(mawk 'NR==4' /run/dietpi/.network)
				G_CONFIG_INJECT 'host =' "host = $ffsync_ip" $ffsync_conf
				G_CONFIG_INJECT 'port =' 'port = 5000' $ffsync_conf # To be failsafe if the default changes in future releases
				G_CONFIG_INJECT 'public_url =' "public_url = http://$ffsync_ip:5000/" $ffsync_conf
				G_CONFIG_INJECT 'sqluri = sqlite:' 'sqluri = sqlite:////mnt/dietpi_userdata/firefox-sync/FF-Sync-DB.db' $ffsync_conf

			fi

			cat << _EOF_ > /etc/systemd/system/firefox-sync.service
[Unit]
Description=Firefox Sync Server (DietPi)
Documentation=https://github.com/mozilla-services/syncserver
Wants=network-online.target
After=network-online.target dietpi-boot.service

[Service]
User=ffsync
WorkingDirectory=/mnt/dietpi_userdata/firefox-sync
ExecStart=/opt/firefox-sync/local/bin/gunicorn --paste $ffsync_conf

# Hardening
ProtectSystem=strict
ProtectHome=true
PrivateDevices=true
ProtectKernelTunables=true
ProtectControlGroups=true
ReadWritePaths=/mnt/dietpi_userdata/firefox-sync

[Install]
WantedBy=multi-user.target
_EOF_
			# Permissions
			G_EXEC chown -R ffsync:ffsync /mnt/dietpi_userdata/firefox-sync

		fi

		software_id=183 # Bitwarden_RS
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# User
			Create_User -d /mnt/dietpi_userdata/bitwarden_rs bitwarden_rs

			# Data and config dir
			G_EXEC mkdir -p /mnt/dietpi_userdata/bitwarden_rs

			# Config: Preserve old
			if [[ ! -f '/mnt/dietpi_userdata/bitwarden_rs/bitwarden_rs.env' ]]
			then
				G_EXEC cp /opt/bitwarden_rs/.env.template /mnt/dietpi_userdata/bitwarden_rs/bitwarden_rs.env
				G_CONFIG_INJECT 'DATA_FOLDER=' 'DATA_FOLDER=/mnt/dietpi_userdata/bitwarden_rs' /mnt/dietpi_userdata/bitwarden_rs/bitwarden_rs.env
				# Create TLS certificate for web vault: Currently only RSA is supported. Add SAN with local IP and hostname, required for the client to accept the connection.
				G_EXEC_OUTPUT=1 G_EXEC openssl req -reqexts SAN -subj '/CN=DietPi Bitwarden_RS' -config <(cat /etc/ssl/openssl.cnf <(echo -ne "[SAN]\nsubjectAltName=DNS:$(</etc/hostname),IP:$(mawk 'NR==4' /run/dietpi/.network)\nbasicConstraints=CA:TRUE,pathlen:0"))\
					-x509 -days 7200 -sha256 -extensions SAN -out /mnt/dietpi_userdata/bitwarden_rs/cert.pem\
					-newkey rsa:4096 -nodes -keyout /mnt/dietpi_userdata/bitwarden_rs/privkey.pem
				G_CONFIG_INJECT 'ROCKET_TLS=' 'ROCKET_TLS={certs="./cert.pem",key="./privkey.pem"}' /mnt/dietpi_userdata/bitwarden_rs/bitwarden_rs.env
				G_CONFIG_INJECT 'ROCKET_PORT=' 'ROCKET_PORT=8001' /mnt/dietpi_userdata/bitwarden_rs/bitwarden_rs.env # Avoid port conflict with WebIOPi, IceCast and Koel
				G_CONFIG_INJECT 'WEB_VAULT_ENABLED=' 'WEB_VAULT_ENABLED=true' /mnt/dietpi_userdata/bitwarden_rs/bitwarden_rs.env
			fi

			# Permissions
			G_EXEC chown -R bitwarden_rs:bitwarden_rs /mnt/dietpi_userdata/bitwarden_rs
			G_EXEC chmod +x /opt/bitwarden_rs/target/release/bitwarden_rs

			# Service: https://github.com/dani-garcia/bitwarden_rs/wiki/Setup-as-a-systemd-service
			cat << '_EOF_' > /etc/systemd/system/bitwarden_rs.service
[Unit]
Description=Bitwarden Server (Rust Edition)
Documentation=https://github.com/dani-garcia/bitwarden_rs

After=dietpi-boot.service network.target

# Restart attempt only 5 times
StartLimitIntervalSec=500
StartLimitBurst=5

[Service]
# Server sometimes fails to start on startup, this should fix it
Restart=on-failure
RestartSec=5s
# The user bitwarden_rs is run under. the working directory (see below) should allow write and read access to this user
User=bitwarden_rs
# The location of the .env file for configuration
EnvironmentFile=/mnt/dietpi_userdata/bitwarden_rs/bitwarden_rs.env
# The location of the compiled binary
ExecStart=/opt/bitwarden_rs/target/release/bitwarden_rs
# Set reasonable connection and process limits
LimitNOFILE=1048576
LimitNPROC=64
# Isolate bitwarden_rs from the rest of the system
PrivateTmp=true
PrivateDevices=true
ProtectHome=true
ProtectSystem=strict
# Only allow writes to the following directory and set it to the working directory (user and password data are stored here)
WorkingDirectory=/mnt/dietpi_userdata/bitwarden_rs
ReadWritePaths=/mnt/dietpi_userdata/bitwarden_rs
# Allow bitwarden_rs to bind ports in the range of 0-1024
AmbientCapabilities=CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target
_EOF_
		fi

		software_id=141 # Spotify Connect Web
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			cat << '_EOF_' > /etc/systemd/system/spotify-connect-web.service
[Unit]
Description=Spotify Connect Web (DietPi)
Wants=network-online.target
After=network-online.target sound.target

[Service]
WorkingDirectory=/mnt/dietpi_userdata/spotify-connect-web
ExecStart=/mnt/dietpi_userdata/spotify-connect-web/spotify-connect-web

[Install]
WantedBy=multi-user.target
_EOF_
		fi

		software_id=142 # CouchPotato
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			cp /etc/couchpotato/init/ubuntu.default /etc/default/couchpotato
			sed -i '/CP_USER=/c\CP_USER=root' /etc/default/couchpotato
			sed -i '/CP_HOME=/c\CP_HOME=/etc/couchpotato' /etc/default/couchpotato
			sed -i '/CP_DATA=/c\CP_DATA=/mnt/dietpi_userdata/couchpotato' /etc/default/couchpotato

			mkdir -p /mnt/dietpi_userdata/couchpotato
			#Create_User -d /mnt/dietpi_userdata/couchpotato couchpotato

			cp /etc/couchpotato/init/ubuntu /etc/init.d/couchpotato

		fi

		software_id=143 # Koel
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# User
			Create_User -g dietpi -d /mnt/dietpi_userdata/koel koel

			Download_Test_Media

			G_EXEC cd /mnt/dietpi_userdata/koel

			# Create database and apply to config file, if it does not exist yet
			if [[ ! -d '/mnt/dietpi_userdata/mysql/koel' ]]; then

				/boot/dietpi/func/create_mysql_db koel koel "$GLOBAL_PW"
				G_CONFIG_INJECT 'DB_CONNECTION=' 'DB_CONNECTION=mysql' .env
				G_CONFIG_INJECT 'DB_HOST=' 'DB_HOST=127.0.0.1' .env
				G_CONFIG_INJECT 'DB_DATABASE=' 'DB_DATABASE=koel' .env
				G_CONFIG_INJECT 'DB_USERNAME=' 'DB_USERNAME=koel' .env
				GCI_PASSWORD=1 G_CONFIG_INJECT 'DB_PASSWORD=' "DB_PASSWORD=$GLOBAL_PW" .env
				# ADMIN env vars are not used any more, user prompt will ask for info.
				#G_CONFIG_INJECT 'ADMIN_EMAIL=' 'ADMIN_EMAIL=dietpi@dietpi.com' .env
				#G_CONFIG_INJECT 'ADMIN_NAME=' 'ADMIN_NAME=admin' .env
				#GCI_PASSWORD=1 G_CONFIG_INJECT 'ADMIN_PASSWORD=' "ADMIN_PASSWORD=$GLOBAL_PW" .env
				G_CONFIG_INJECT 'FFMPEG_PATH=' "FFMPEG_PATH=$(command -v ffmpeg)" .env

			# Else assure database server runs
			else

				G_EXEC systemctl restart mariadb

			fi

			$PHP_NAME artisan koel:init
			$PHP_NAME artisan key:generate # Required with current setup, else .env APP_KEY is missing, leading to 500 on web UI login attempt
			#$PHP_NAME artisan db:seed --force

			G_EXEC cd /tmp/$G_PROGRAM_NAME

			cat << _EOF_ > /etc/systemd/system/koel.service
[Unit]
Description=Koel (DietPi)

[Service]
User=koel
WorkingDirectory=/mnt/dietpi_userdata/koel
ExecStart=$(command -v $PHP_NAME) /mnt/dietpi_userdata/koel/artisan serve --host 0.0.0.0

[Install]
WantedBy=multi-user.target
_EOF_
			# Permissions
			chown -R koel:dietpi /mnt/dietpi_userdata/koel

		fi

		software_id=144 # Sonarr
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Data dir
			G_EXEC mkdir -p /mnt/dietpi_userdata/sonarr

			# User
			Create_User -g dietpi -d /mnt/dietpi_userdata/sonarr sonarr

			# Service: https://github.com/Sonarr/Sonarr/blob/phantom-develop/distribution/debian/sonarr.service
			cat << _EOF_ > /etc/systemd/system/sonarr.service
[Unit]
Description=Sonarr Daemon (DietPi)
Wants=network-online.target
After=network-online.target dietpi-boot.service

[Service]
SyslogIdentifier=Sonarr
User=sonarr
UMask=002
ExecStart=$(command -v mono) -O=-aot /opt/NzbDrone/NzbDrone.exe -nobrowser -data=/mnt/dietpi_userdata/sonarr
TimeoutStopSec=20
KillMode=process
Restart=on-failure

# Hardening
ProtectSystem=strict
ProtectHome=true
PrivateDevices=true
ProtectKernelTunables=true
ProtectControlGroups=true
ReadWritePaths=/opt/NzbDrone /mnt /media /var/log/sonarr /tmp

[Install]
WantedBy=multi-user.target
_EOF_
			# Logs to RAM
			G_EXEC rm -Rf /mnt/dietpi_userdata/sonarr/logs*
			G_EXEC mkdir -p /var/log/sonarr
			G_EXEC ln -s /var/log/sonarr /mnt/dietpi_userdata/sonarr/logs
			G_EXEC ln -s /var/log/sonarr/logs.db /mnt/dietpi_userdata/sonarr/logs.db
			G_EXEC ln -s /var/log/sonarr/logs.db-shm /mnt/dietpi_userdata/sonarr/logs.db-shm
			G_EXEC ln -s /var/log/sonarr/logs.db-wal /mnt/dietpi_userdata/sonarr/logs.db-wal

			# Permissions
			G_EXEC chown -R sonarr:dietpi /mnt/dietpi_userdata/sonarr /opt/NzbDrone /var/log/sonarr

		fi

		software_id=145 # Radarr
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Data dir
			G_EXEC mkdir -p /mnt/dietpi_userdata/radarr

			# User
			Create_User -g dietpi -d /mnt/dietpi_userdata/radarr radarr

			# Service: https://github.com/Radarr/Radarr/wiki/Autostart-on-Linux#service-file
			cat << '_EOF_' > /etc/systemd/system/radarr.service
[Unit]
Description=Radarr Daemon (DietPi)
Wants=network-online.target
After=network-online.target dietpi-boot.service

[Service]
SyslogIdentifier=Radarr
User=radarr
UMask=002
ExecStart=/opt/radarr/Radarr -nobrowser -data=/mnt/dietpi_userdata/radarr
TimeoutStopSec=20
KillMode=process
Restart=on-failure

# Hardening
ProtectSystem=strict
ProtectHome=true
PrivateDevices=true
ProtectKernelTunables=true
ProtectControlGroups=true
ReadWritePaths=/opt/radarr /mnt /media /var/log/radarr /tmp

[Install]
WantedBy=multi-user.target
_EOF_
			# - ARMv6 devices use Mono
			(( $G_HW_ARCH == 1 )) && G_CONFIG_INJECT 'ExecStart=' "ExecStart=$(command -v mono) -O=-aot /opt/radarr/Radarr.exe -nobrowser -data=/mnt/dietpi_userdata/radarr" /etc/systemd/system/radarr.service

			# Logs to RAM
			G_EXEC rm -Rf /mnt/dietpi_userdata/radarr/logs*
			G_EXEC mkdir -p /var/log/radarr
			G_EXEC ln -s /var/log/radarr /mnt/dietpi_userdata/radarr/logs
			G_EXEC ln -s /var/log/radarr/logs.db /mnt/dietpi_userdata/radarr/logs.db
			G_EXEC ln -s /var/log/radarr/logs.db-shm /mnt/dietpi_userdata/radarr/logs.db-shm
			G_EXEC ln -s /var/log/radarr/logs.db-wal /mnt/dietpi_userdata/radarr/logs.db-wal

			# Permissions
			G_EXEC chown -R radarr:dietpi /mnt/dietpi_userdata/radarr /opt/radarr /var/log/radarr

		fi

		software_id=106 # Lidarr
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Data dir
			G_EXEC mkdir -p /mnt/dietpi_userdata/lidarr

			# User
			Create_User -g dietpi -d /mnt/dietpi_userdata/lidarr lidarr

			# Service: https://github.com/lidarr/Lidarr/wiki/Autostart-on-Linux#systemd
			cat << _EOF_ > /etc/systemd/system/lidarr.service
[Unit]
Description=Lidarr Daemon (DietPi)
Wants=network-online.target
After=network-online.target dietpi-boot.service

[Service]
SyslogIdentifier=Lidarr
User=lidarr
UMask=002
ExecStart=$(command -v mono) -O=-aot /opt/Lidarr/Lidarr.exe -nobrowser -data=/mnt/dietpi_userdata/lidarr
TimeoutStopSec=20
KillMode=process
Restart=on-failure

# Hardening
ProtectSystem=strict
ProtectHome=true
PrivateDevices=true
ProtectKernelTunables=true
ProtectControlGroups=true
ReadWritePaths=/opt/Lidarr /mnt /media /var/log/lidarr /tmp

[Install]
WantedBy=multi-user.target
_EOF_
			# Logs to RAM
			G_EXEC rm -Rf /mnt/dietpi_userdata/lidarr/logs*
			G_EXEC mkdir -p /var/log/lidarr
			G_EXEC ln -s /var/log/lidarr /mnt/dietpi_userdata/lidarr/logs
			G_EXEC ln -s /var/log/lidarr/logs.db /mnt/dietpi_userdata/lidarr/logs.db
			G_EXEC ln -s /var/log/lidarr/logs.db-shm /mnt/dietpi_userdata/lidarr/logs.db-shm
			G_EXEC ln -s /var/log/lidarr/logs.db-wal /mnt/dietpi_userdata/lidarr/logs.db-wal

			# Permissions
			G_EXEC chown -R lidarr:dietpi /mnt/dietpi_userdata/lidarr /opt/Lidarr /var/log/lidarr

		fi

		software_id=180 # Bazarr
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Data dir
			G_EXEC mkdir -p /mnt/dietpi_userdata/bazarr

			# User
			Create_User -g dietpi -d /mnt/dietpi_userdata/bazarr bazarr

			# Service: https://github.com/morpheus65535/bazarr/wiki/Autostart-on-Linux-Windows-MacOS-FreeBSD#systemd-service-file-for-debian-ubuntu
			cat << _EOF_ > /etc/systemd/system/bazarr.service
[Unit]
Description=Bazarr Daemon (DietPi)
Wants=network-online.target
After=network-online.target dietpi-boot.service sonarr.service radarr.service

[Service]
SyslogIdentifier=Bazarr
User=bazarr
UMask=002
ExecStart=$(command -v python3) /opt/bazarr/bazarr.py -c /mnt/dietpi_userdata/bazarr

# Hardening
ProtectSystem=strict
ProtectHome=true
PrivateDevices=true
ProtectKernelTunables=true
ProtectControlGroups=true
ReadWritePaths=/opt/bazarr /mnt /media /var/log/bazarr /tmp

[Install]
WantedBy=multi-user.target
_EOF_
			# Logs to RAM
			G_EXEC rm -Rf /mnt/dietpi_userdata/bazarr/log
			G_EXEC mkdir -p /var/log/bazarr
			G_EXEC ln -s /var/log/bazarr /mnt/dietpi_userdata/bazarr/log

			# Permissions
			G_EXEC chown -R bazarr:dietpi /mnt/dietpi_userdata/bazarr /opt/bazarr /var/log/bazarr

			# On fresh install, pre-configure for Sonarr and Radarr when found
			if (( ${aSOFTWARE_INSTALL_STATE[144]} + ${aSOFTWARE_INSTALL_STATE[145]} > 0 )) && Create_Config /mnt/dietpi_userdata/bazarr/config/config.ini bazarr
			then
				# Sonarr
				if (( ${aSOFTWARE_INSTALL_STATE[144]} > 0 )) && CREATE_CONFIG_CONTENT='ApiKey' Create_Config /mnt/dietpi_userdata/sonarr/config.xml sonarr
				then
					local port=$(sed -nE '/<Port>[0-9]+<\/Port>/{s/^.*<Port>([0-9]+)<\/Port>.*$/\1/p;q}' /mnt/dietpi_userdata/sonarr/config.xml)
					local apikey=$(sed -nE '/<ApiKey>.+<\/ApiKey>/{s/^.*<ApiKey>(.+)<\/ApiKey>.*$/\1/p;q}' /mnt/dietpi_userdata/sonarr/config.xml)
					G_EXEC sed -i '/\[sonarr\]/,/^$/s/^ip = .*$/ip = 127.0.0.1/' /mnt/dietpi_userdata/bazarr/config/config.ini
					G_EXEC sed -i "/\[sonarr\]/,/^$/s/^port = .*$/port = $port/" /mnt/dietpi_userdata/bazarr/config/config.ini
					G_EXEC sed -i '/\[sonarr\]/,/^$/s/^base_url = .*$/base_url = \//' /mnt/dietpi_userdata/bazarr/config/config.ini
					G_EXEC sed -i "/\[sonarr\]/,/^$/s/^apikey = .*$/apikey = $apikey/" /mnt/dietpi_userdata/bazarr/config/config.ini
					G_CONFIG_INJECT 'use_sonarr[[:blank:]]*=' 'use_sonarr = True' /mnt/dietpi_userdata/bazarr/config/config.ini '\[general\]'
				fi
				# Radarr
				if (( ${aSOFTWARE_INSTALL_STATE[145]} > 0 )) && CREATE_CONFIG_CONTENT='ApiKey' Create_Config /mnt/dietpi_userdata/radarr/config.xml radarr
				then
					local port=$(sed -nE '/<Port>[0-9]+<\/Port>/{s/^.*<Port>([0-9]+)<\/Port>.*$/\1/p;q}' /mnt/dietpi_userdata/radarr/config.xml)
					local apikey=$(sed -nE '/<ApiKey>.+<\/ApiKey>/{s/^.*<ApiKey>(.+)<\/ApiKey>.*$/\1/p;q}' /mnt/dietpi_userdata/radarr/config.xml)
					G_EXEC sed -i '/\[radarr\]/,/^$/s/^ip = .*$/ip = 127.0.0.1/' /mnt/dietpi_userdata/bazarr/config/config.ini
					G_EXEC sed -i "/\[radarr\]/,/^$/s/^port = .*$/port = $port/" /mnt/dietpi_userdata/bazarr/config/config.ini
					G_EXEC sed -i '/\[radarr\]/,/^$/s/^base_url = .*$/base_url = \//' /mnt/dietpi_userdata/bazarr/config/config.ini
					G_EXEC sed -i "/\[radarr\]/,/^$/s/^apikey = .*$/apikey = $apikey/" /mnt/dietpi_userdata/bazarr/config/config.ini
					G_CONFIG_INJECT 'use_radarr[[:blank:]]*=' 'use_radarr = True' /mnt/dietpi_userdata/bazarr/config/config.ini '\[general\]'
				fi
			fi
		fi

		software_id=146 # Tautulli
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# User
			Create_User -d /mnt/dietpi_userdata/tautulli tautulli

			# Directory
			G_EXEC mkdir -p /mnt/dietpi_userdata/tautulli

			# Service: https://github.com/Tautulli/Tautulli/blob/master/init-scripts/init.systemd
			cat << _EOF_ > /etc/systemd/system/tautulli.service
[Unit]
Description=Tautulli - Stats for Plex Media Server usage (DietPi)
Wants=network-online.target
After=network-online.target dietpi-boot.service

[Service]
User=tautulli
ExecStart=$(command -v python3) /opt/tautulli/Tautulli.py -q --nolaunch --config /mnt/dietpi_userdata//tautulli/config.ini --datadir /mnt/dietpi_userdata/tautulli

[Install]
WantedBy=multi-user.target
_EOF_
			# Permissions
			G_EXEC chown -R tautulli:tautulli /{opt,mnt/dietpi_userdata}/tautulli

		fi

		software_id=147 # Jackett
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# User
			Create_User -d /opt/jackett jackett

			# Permissions
			chown -R jackett:jackett /opt/jackett

			# Service: https://github.com/Jackett/Jackett/wiki/systemd-service
			# - Wrap execution into shell to work around: https://github.com/Jackett/Jackett/issues/5208
			cat << '_EOF_' > /etc/systemd/system/jackett.service
[Unit]
Description=Jackett (DietPi)
Wants=network-online.target
After=network-online.target dietpi-boot.service

[Service]
SyslogIdentifier=jackett
User=jackett
WorkingDirectory=/opt/jackett
Environment=XDG_CONFIG_HOME=/opt/jackett
ExecStart=/bin/dash -c '/opt/jackett/jackett --NoRestart; while pgrep -u jackett JackettUpdater > /dev/null; do sleep 1; done'
Restart=always
RestartSec=5
TimeoutStopSec=30

# Hardening
ProtectSystem=strict
ProtectHome=true
PrivateDevices=true
PrivateTmp=true
ReadWritePaths=/opt/jackett

[Install]
WantedBy=multi-user.target
_EOF_
			# - ARMv6 devices use Mono
			(( $G_HW_ARCH == 1 )) && G_CONFIG_INJECT 'ExecStart=' "ExecStart=$(command -v mono) -O=-aot /opt/jackett/JackettConsole.exe --NoRestart" /etc/systemd/system/jackett.service

		fi

		software_id=149 # NZBGet
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			Create_User -g dietpi -d /mnt/dietpi_userdata/nzbget nzbget

			G_BACKUP_FP /mnt/dietpi_userdata/nzbget/nzbget.conf
			sed -i '/MainDir=/c\MainDir=/mnt/dietpi_userdata/downloads' /mnt/dietpi_userdata/nzbget/nzbget.conf
			sed -i '/DestDir=/c\DestDir=/mnt/dietpi_userdata/downloads/complete' /mnt/dietpi_userdata/nzbget/nzbget.conf
			sed -i '/LogFile=/c\LogFile=/var/log/nzbget.log' /mnt/dietpi_userdata/nzbget/nzbget.conf
			sed -i '/ControlUsername=/c\ControlUsername=admin' /mnt/dietpi_userdata/nzbget/nzbget.conf
			sed -i "/ControlPassword=/c\ControlPassword=$GLOBAL_PW" /mnt/dietpi_userdata/nzbget/nzbget.conf

			# Umask: https://github.com/MichaIng/DietPi/issues/1999
			sed -i '/UMask=/c\UMask=0002' /mnt/dietpi_userdata/nzbget/nzbget.conf

			# Optimisations
			sed -i '/Server1.Cipher=/c\Server1.Cipher=RC4-MD5' /mnt/dietpi_userdata/nzbget/nzbget.conf
			sed -i '/CrcCheck=/c\CrcCheck=no' /mnt/dietpi_userdata/nzbget/nzbget.conf
			sed -i '/ParScan=/c\ParScan=limited' /mnt/dietpi_userdata/nzbget/nzbget.conf
			sed -i "/ParThreads=/c\ParThreads=$G_HW_CPU_CORES" /mnt/dietpi_userdata/nzbget/nzbget.conf
			sed -i '/DebugTarget=/c\DebugTarget=none' /mnt/dietpi_userdata/nzbget/nzbget.conf
			sed -i '/CrashTrace=/c\CrashTrace=no' /mnt/dietpi_userdata/nzbget/nzbget.conf
			sed -i '/DetailTarget=/c\DetailTarget=none' /mnt/dietpi_userdata/nzbget/nzbget.conf
			sed -i "/ParBuffer=/c\ParBuffer=$(Optimise_BitTorrent 0)" /mnt/dietpi_userdata/nzbget/nzbget.conf
			sed -i "/ArticleCache=/c\ArticleCache=$(Optimise_BitTorrent 0)" /mnt/dietpi_userdata/nzbget/nzbget.conf
			sed -i "/WriteBuffer=/c\WriteBuffer=$(Optimise_BitTorrent 0)" /mnt/dietpi_userdata/nzbget/nzbget.conf

			# Service
			cat << '_EOF_' > /etc/systemd/system/nzbget.service
[Unit]
Description=NZBget (DietPi)

[Service]
Type=forking
User=nzbget
WorkingDirectory=/mnt/dietpi_userdata/nzbget
ExecStart=/mnt/dietpi_userdata/nzbget/nzbget -D

[Install]
WantedBy=multi-user.target
_EOF_
			# Permissions
			chown -R nzbget:root /mnt/dietpi_userdata/nzbget

		fi

		software_id=155 # HTPC Manager
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Service
			cat << _EOF_ > /etc/systemd/system/htpc-manager.service
[Unit]
Description=HTPC Manager (DietPi)
Wants=network-online.target
After=network-online.target dietpi-boot.service

[Service]
SyslogIdentifier=HTPC Manager
ExecStart=$(command -v python3) /mnt/dietpi_userdata/htpc-manager/Htpc.py

[Install]
WantedBy=multi-user.target
_EOF_
		fi

		software_id=153 # OctoPrint
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 ))
		then
			Banner_Configuration

			# Service: https://github.com/OctoPrint/OctoPrint/blob/master/scripts/octoprint.service
			cat << '_EOF_' > /etc/systemd/system/octoprint.service
[Unit]
Description=OctoPrint (DietPi)
Documentation=https://dietpi.com/docs/software/printing/#octoprint
Wants=network-online.target
After=network-online.target dietpi-boot.service mjpg-streamer.service

[Service]
Environment="LC_ALL=C.UTF-8" "LANG=C.UTF-8"
User=octoprint
ExecStart=/mnt/dietpi_userdata/octoprint/.local/bin/octoprint serve

[Install]
WantedBy=multi-user.target
_EOF_
			# CLI alias
			echo "alias octoprint='sudo -u octoprint /mnt/dietpi_userdata/octoprint/.local/bin/octoprint'" > /etc/bashrc.d/dietpi-octoprint.sh

			# Apply service and system commands: Allow execution via specific sudoers config
			echo "octoprint ALL=NOPASSWD: $(command -v systemctl) restart octoprint, $(command -v reboot), $(command -v poweroff)" > /etc/sudoers.d/octoprint
			G_EXEC sudo -u octoprint /mnt/dietpi_userdata/octoprint/.local/bin/octoprint config set server.commands.serverRestartCommand 'sudo systemctl restart octoprint'
			G_EXEC sudo -u octoprint /mnt/dietpi_userdata/octoprint/.local/bin/octoprint config set server.commands.systemRestartCommand 'sudo reboot'
			G_EXEC sudo -u octoprint /mnt/dietpi_userdata/octoprint/.local/bin/octoprint config set server.commands.systemShutdownCommand 'sudo poweroff'

			# mjpg-streamer: Configure OctoPrint to use it, if installed
			if (( ${aSOFTWARE_INSTALL_STATE[137]} > 0 ))
			then
				G_DIETPI-NOTIFY 2 'Configuring OctoPrint to use mjpg-streamer for webcam support'
				G_EXEC sudo -u octoprint /mnt/dietpi_userdata/octoprint/.local/bin/octoprint config set webcam.stream "http://$(mawk 'NR==4' /run/dietpi/.network):8082/?action=stream"
				G_EXEC sudo -u octoprint /mnt/dietpi_userdata/octoprint/.local/bin/octoprint config set webcam.snapshot 'http://127.0.0.1:8082/?action=snapshot'
				G_EXEC sudo -u octoprint /mnt/dietpi_userdata/octoprint/.local/bin/octoprint config set webcam.ffmpeg "$(command -v ffmpeg)"
			fi
		fi

		software_id=154 # Roon Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Service
			cat << '_EOF_' > /etc/systemd/system/roonserver.service
[Unit]
Description=Roon Server (DietPi)
Wants=network-online.target
After=network-online.target dietpi-boot.service

[Service]
SyslogIdentifier=roonserver
Environment=ROON_DATAROOT=/mnt/dietpi_userdata/roonserver
Group=dietpi
ExecStart=/mnt/dietpi_userdata/roonserver/start.sh

[Install]
WantedBy=multi-user.target
_EOF_
			# Permissions
			#chown -R roon:dietpi /mnt/dietpi_userdata/roonserver

		fi

		software_id=156 # Steam
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Move data dir to dietpi_userdata
			if [[ -d '/mnt/dietpi_userdata/steam' ]]
			then
				G_EXEC rm -Rf /root/.steam

			elif [[ -d '/root/.steam' ]]
			then
				G_EXEC mv /root/.steam /mnt/dietpi_userdata/steam

			else
				G_EXEC mkdir -p /mnt/dietpi_userdata/steam
			fi
			G_EXEC ln -s /mnt/dietpi_userdata/steam /root/.steam

			# Desktop shortcut
			Create_Desktop_Shortcut steam

		fi

		software_id=157 # Home Assistant
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			cat << '_EOF_' > /etc/systemd/system/home-assistant.service
[Unit]
Description=Home Assistant (DietPi)
Wants=network-online.target
After=network-online.target mariadb.service

[Service]
SyslogIdentifier=homeassistant
User=homeassistant
ExecStart=/home/homeassistant/homeassistant-start.sh

[Install]
WantedBy=multi-user.target
_EOF_
			# Link to the default ha location for the homeassistant user, this makes
			# the configuration avaliable for the user to edit. Configuration generated
			# when service is started at /home/homeassistant/.homeassistant
			# - Move default home location to userdata, if not existing yet
			if [[ ! -d '/mnt/dietpi_userdata/homeassistant' ]]; then

				[[ -d '/home/homeassistant/.homeassistant' ]] && G_EXEC mv /home/homeassistant/.homeassistant /mnt/dietpi_userdata/homeassistant
				G_EXEC mkdir -p /mnt/dietpi_userdata/homeassistant

			fi
			rm -Rf /home/homeassistant/.homeassistant
			G_EXEC ln -s /mnt/dietpi_userdata/homeassistant /home/homeassistant/.homeassistant
			G_EXEC chown -R homeassistant:homeassistant /mnt/dietpi_userdata/homeassistant

		fi

		software_id=140 # Domoticz: https://www.domoticz.com/wiki/Linux
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Data dir
			G_EXEC mkdir -p /mnt/dietpi_userdata/domoticz

			# Config file
			if [[ ! -f '/mnt/dietpi_userdata/domoticz/domoticz.conf' ]]; then

				G_EXEC cp /opt/domoticz/scripts/domoticz.conf /mnt/dietpi_userdata/domoticz/domoticz.conf
				G_CONFIG_INJECT 'http_port=' 'http_port=8124' /mnt/dietpi_userdata/domoticz/domoticz.conf
				G_CONFIG_INJECT 'ssl_port=' 'ssl_port=8424' /mnt/dietpi_userdata/domoticz/domoticz.conf
				G_CONFIG_INJECT 'ssl_cert=' 'ssl_cert=/opt/domoticz/server_cert.pem' /mnt/dietpi_userdata/domoticz/domoticz.conf
				G_CONFIG_INJECT 'ssl_key=' 'ssl_key=/opt/domoticz/server_cert.pem' /mnt/dietpi_userdata/domoticz/domoticz.conf
				G_CONFIG_INJECT 'ssl_dhparam=' 'ssl_dhparam=/opt/domoticz/server_cert.pem' /mnt/dietpi_userdata/domoticz/domoticz.conf
				G_CONFIG_INJECT 'loglevel=' 'loglevel=error' /mnt/dietpi_userdata/domoticz/domoticz.conf
				G_CONFIG_INJECT 'syslog=' 'syslog=local7' /mnt/dietpi_userdata/domoticz/domoticz.conf
				G_CONFIG_INJECT 'dbase_file=' "dbase_file=/mnt/dietpi_userdata/domoticz/domoticz.db" /mnt/dietpi_userdata/domoticz/domoticz.conf
				G_CONFIG_INJECT 'app_path=' 'app_path=/opt/domoticz' /mnt/dietpi_userdata/domoticz/domoticz.conf
				G_CONFIG_INJECT 'userdata_path=' "userdata_path=/mnt/dietpi_userdata/domoticz" /mnt/dietpi_userdata/domoticz/domoticz.conf

			fi

			# User
			Create_User -G dialout -d /mnt/dietpi_userdata/domoticz domoticz

			# Permissions
			G_EXEC chown -R domoticz:domoticz /opt/domoticz /mnt/dietpi_userdata/domoticz
			
			# Copy scripts directory to data dir: https://dietpi.com/phpbb/viewtopic.php?t=8627
			G_EXEC cp -a /opt/domoticz/scripts /mnt/dietpi_userdata/domoticz/

			# Service
			cat << '_EOF_' > /etc/systemd/system/domoticz.service
[Unit]
Description=Domoticz (DietPi)
Wants=network-online.target
After=network-online.target dietpi-boot.service

[Service]
User=domoticz
ExecStart=/opt/domoticz/domoticz -f /mnt/dietpi_userdata/domoticz/domoticz.conf

[Install]
WantedBy=multi-user.target
_EOF_

		fi

		software_id=158 # MinIO
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Config
			[[ -f '/etc/default/minio' ]] || cat << '_EOF_' > /etc/default/minio
# Default file path
MINIO_VOLUMES="/mnt/dietpi_userdata/minio-data"
# Use if you want to run MinIO on a custom port.
#MINIO_OPTS="--address :9199"
# Access key of the server.
#MINIO_ACCESS_KEY=Server-Access-Key
# Secret key of the server.
#MINIO_SECRET_KEY=Server-Secret-Key
_EOF_
		fi

		software_id=162 # Docker
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Set Docker service to "simple": https://github.com/MichaIng/DietPi/issues/2238#issuecomment-439474766
			G_EXEC mkdir -p /lib/systemd/system/docker.service.d
			echo -e '[Service]\nType=simple' > /lib/systemd/system/docker.service.d/dietpi-simple.conf

			# Config: https://docs.docker.com/engine/reference/commandline/dockerd/#daemon-configuration-file
			# - Move Docker containers to dietpi_userdata
			# - Log to systemd-journald (journalctl) by default with reduced log level: https://github.com/MichaIng/DietPi/issues/2388
			#	+ containerd: https://github.com/docker/docker.github.io/issues/9091
			G_CONFIG_INJECT '\[debug\]' '[debug]' /etc/containerd/config.toml
			GCI_PRESERVE=1 G_CONFIG_INJECT 'level[[:blank:]]*=' '  level = "warn"' /etc/containerd/config.toml '^\[debug\]'
			G_EXEC mkdir -p /mnt/dietpi_userdata/docker-data
			if [[ -f '/etc/docker/daemon.json' ]]; then

				GCI_PRESERVE=1 G_CONFIG_INJECT '"data-root":' '    "data-root": "/mnt/dietpi_userdata/docker-data",' /etc/docker/daemon.json '^\{([[:space:]]|$)'
				GCI_PRESERVE=1 G_CONFIG_INJECT '"log-driver":' '    "log-driver": "journald",' /etc/docker/daemon.json '^\{([[:space:]]|$)'
				GCI_PRESERVE=1 G_CONFIG_INJECT '"log-level":' '    "log-level": "warn",' /etc/docker/daemon.json '^\{([[:space:]]|$)'
				GCI_PRESERVE=1 G_CONFIG_INJECT '"debug":' '    "debug": false,' /etc/docker/daemon.json '^\{([[:space:]]|$)'

			else

				G_EXEC mkdir -p /etc/docker
				echo '{
    "data-root": "/mnt/dietpi_userdata/docker-data",
    "log-driver": "journald",
    "log-level": "warn",
    "debug": false
}' > /etc/docker/daemon.json

			fi

		fi

		software_id=185 # Portainer
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Restart Docker service to be able to deploy the container
			G_DIETPI-NOTIFY 2 'Docker will be restarted to be able to deploy the container.'
			G_EXEC systemctl daemon-reload
			G_EXEC systemctl restart docker

			# Check for existing portainer installation
			if [[ -d '/mnt/dietpi_userdata/docker-data/volumes/portainer_data' ]]; then
				G_DIETPI-NOTIFY 2 'Portainer detected, container will be dropped before reinstallation.'
				G_EXEC docker rm "$(docker ps -a | mawk '/portainer\/portainer(-ce)?( |$)/{print $1}')" --force
				G_EXEC docker rmi "$(docker images -a | mawk '/portainer\/portainer(-ce)?( |$)/{print $3}')"
			else
				# Create data directory
				G_EXEC docker volume create portainer_data
			fi

			# Deploy the portainer container
			G_DIETPI-NOTIFY 2 'Portainer will be deployed now. This could take a while...'
			G_EXEC_OUTPUT=1 G_EXEC docker run -d -p 9002:9000 --name=portainer --restart=always -v /run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce

		fi

		software_id=161 # FuguHub
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Setup Filestore DietPi appropriate
			# IF already present just create symlink
			if [[ ! -d '/mnt/dietpi_userdata/fuguhub-data' ]]; then

				# Move installed filestore to dietpi userdata
				mkdir -p /mnt/dietpi_userdata/fuguhub-data
				mv /home/bd/disk/* /mnt/dietpi_userdata/fuguhub-data/
				# Set permissions
				chown -R bd:bd /mnt/dietpi_userdata/fuguhub-data/
				#setfacl -R -m u:bd:rwx /mnt/dietpi_userdata/fuguhub-data/

			fi

			# Removed 'actual' folder to make way for symlink
			rm -Rf /home/bd/disk
			# Create symlink
			ln -s /mnt/dietpi_userdata/fuguhub-data /home/bd/disk

		fi

		software_id=164 # Nukkit
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Conig file to autostart -- english default
			G_EXEC curl -sSfL -o /usr/local/bin/nukkit/nukkit.yml https://github.com/Nukkit/Languages/raw/master/eng/nukkit.yml

			# Service
			cat << '_EOF_' > /etc/systemd/system/nukkit.service
[Unit]
Description=nukkit (DietPi)

[Service]
SyslogIdentifier=Nukkit
WorkingDirectory=/usr/local/bin/nukkit
ExecStart=/bin/bash -c 'java -jar /usr/local/bin/nukkit/nukkit.jar'

[Install]
WantedBy=multi-user.target
_EOF_

		fi

		software_id=165	# Gitea
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Logs
			mkdir -p /var/log/gitea

			# Permissions
			chown -R dietpi:dietpi /var/log/gitea /mnt/dietpi_userdata/gitea
			chmod +x /mnt/dietpi_userdata/gitea/gitea

			# Database
			/boot/dietpi/func/create_mysql_db gitea gitea "$GLOBAL_PW"

			# Service
			cat << '_EOF_' > /etc/systemd/system/gitea.service
[Unit]
Description=Gitea (DietPi)

[Service]
User=dietpi
WorkingDirectory=/mnt/dietpi_userdata/gitea/gitea-repositories
Environment=USER=dietpi HOME=/mnt/dietpi_userdata/gitea
ExecStart=/mnt/dietpi_userdata/gitea/gitea web

[Install]
WantedBy=multi-user.target
_EOF_

		fi

		software_id=159 # Allo web UI: 160 for quick reinstall/update
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 || ${aSOFTWARE_INSTALL_STATE[160]} == 1 )); then

			Banner_Configuration

			# Create allo user for GUI
			Create_User -G www-data -d /home/allo -s /bin/bash -p allo allo
			# - Create home dir separately to avoid skeleton creation
			mkdir -p /home/allo
			chown -R allo:allo /home/allo
			# - Grant sudo permissions
			echo 'allo ALL=NOPASSWD: ALL' > /etc/sudoers.d/allo

			# Always Drop DB, and, recreate it, due to error issue with reinstall over the top.
			G_EXEC systemctl restart mariadb
			mysqladmin drop allo_db -f &> /dev/null
			mysql -e 'drop user allo_db@localhost' &> /dev/null

			/boot/dietpi/func/create_mysql_db allo_db allo_db dietpi
			mysql allo_db < /var/www/allo_db.sql
			rm /var/www/allo_db.sql

			# Redirect to web interface by default:
			rm -f /var/www/index\.*
			cat << _EOF_ > /var/www/index.php
<?php
/* Redirect to allo web interface */
\$host  = \$_SERVER['HTTP_HOST'];
\$uri   = rtrim(dirname(\$_SERVER['PHP_SELF']), '/\\\\');
\$extra = 'index.php';
header("Location: http://\$host\$uri/allo/public/\$extra");
exit;
?>
_EOF_
			# Permissions
			chown -R www-data:www-data /var/www/allo
			chmod -R 770 /var/www/allo

		fi

		software_id=163 # GMediaRender
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# User
			Create_User -G audio,render gmrender

			# Service
			cat << _EOF_ > /etc/systemd/system/gmrender.service
[Unit]
Description=GMediaRender (DietPi)
Documentation=https://github.com/hzeller/gmrender-resurrect/blob/master/INSTALL.md#commandline-options
Wants=network-online.target
After=network-online.target sound.target dietpi-boot.service

[Service]
User=gmrender
ExecStartPre=/boot/dietpi/func/obtain_network_details
ExecStartPre=+/bin/dash -c 'systemctl set-environment ACTIVE_IP=\$(mawk 'NR==4' /run/dietpi/.network)'
ExecStart=/usr/local/bin/gmediarender -u '$G_HW_UUID' -f '$(</etc/hostname)' --gstout-audiosink=alsasink --gstout-audiodevice=default --logfile=stdout -I "\$ACTIVE_IP"

[Install]
WantedBy=multi-user.target
_EOF_
		fi

		software_id=166 # Audiophonics Pi-SPC
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			G_EXEC mkdir -p /var/lib/dietpi/dietpi-software/installed/pi-spc

			cat << '_EOF_' > /var/lib/dietpi/dietpi-software/installed/pi-spc/sds.sh
#!/bin/bash
# DietPi version
PATH+=':/root/wiringPi/gpio'

TICKRATE=0.25

echo 'Audiophonics Shutdown script starting...
Asserting pins :
ShutDown : GPIO17=in, Low
BootOK   : GPIO22=out, High
SoftSD   : GPIO04=out, Low'

gpio -g mode 04 out
gpio -g write 04 0
gpio -g mode 17 in
gpio -g write 17 0
gpio -g mode 22 out
gpio -g write 22 1

while :
do

	if (( $(gpio -g read 17) == 1 )); then

		G_DIETPI-NOTIFY 0 'Audiophonics Pi-SPC: Power off requested. Shutting down system.'
		sudo poweroff
		#sudo shutdown -h -P now
		break

	fi

	sleep $TICKRATE

done

exit 0
_EOF_
			G_EXEC chmod +x /var/lib/dietpi/dietpi-software/installed/pi-spc/sds.sh

			cat << '_EOF_' > /etc/systemd/system/pi-spc.service
[Unit]
Description=Audiophonics Pi-SPC (DietPi)

[Service]
StandardOutput=tty
ExecStart=/var/lib/dietpi/dietpi-software/installed/pi-spc/sds.sh

[Install]
WantedBy=multi-user.target
_EOF_
			#G_CONFIG_INJECT 'dtoverlay=gpio-shutdown' 'dtoverlay=gpio-shutdown,gpio_pin=22,active_low=0' /boot/config.txt
			#G_CONFIG_INJECT 'dtoverlay=gpio-poweroff' 'dtoverlay=gpio-poweroff,gpio_pin=17' /boot/config.txt

		fi

		software_id=169 # Google AIY
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Symlink userdata location for assistant.json
			ln -sf /mnt/dietpi_userdata/voice-recognizer-raspi/assistant.json /home/dietpi/assistant.json

			# Generate cache dir
			mkdir -p /home/dietpi/.cache/voice-recognizer

			# Setup soundcard
			/boot/dietpi/func/dietpi-set_hardware soundcard googlevoicehat-soundcard

			# Permissions
			chown -R dietpi:dietpi /mnt/dietpi_userdata/voice-recognizer-raspi

		fi

		software_id=176 # Mycroft AI
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Service
			cat << '_EOF_' > /etc/systemd/system/mycroft.service
[Unit]
Description=Mycroft AI (DietPi)

[Service]
Type=forking
User=mycroft
SyslogIdentifier=Mycroft AI
WorkingDirectory=/mnt/dietpi_userdata/mycroft-data
ExecStart=/mnt/dietpi_userdata/mycroft-core/start-mycroft.sh all
ExecStop=/mnt/dietpi_userdata/mycroft-core/stop-mycroft.sh

[Install]
WantedBy=multi-user.target
_EOF_
			# Add Mycroft binaries to $PATH
			G_EXEC eval "echo -e '#!/bin/dash\nexport PATH=$PATH:/mnt/dietpi_userdata/mycroft-core/bin' > /etc/bashrc.d/mycroft.sh"

			# Enable ALSA automatic software conversion plugin
			local soundcard=$(sed -n '/^[[:blank:]]*CONFIG_SOUNDCARD=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
			soundcard=${soundcard%-eq} soundcard=${soundcard%-plug}
			/boot/dietpi/func/dietpi-set_hardware soundcard "$soundcard-plug"

		fi

		software_id=181 # PaperMC
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 ))
		then
			Banner_Configuration

			# User
			Create_User -d /mnt/dietpi_userdata/papermc papermc

			# Bedrock compatibility
			G_WHIP_BUTTON_OK_TEXT='YES'
			G_WHIP_BUTTON_CANCEL_TEXT='NO'
			if G_WHIP_YESNO 'Would you like to install the Geyser and Floodgate plugins for compatibility with Bedrock Edition?\n\nNote that this may be buggy.'
			then
				Download_Install 'https://ci.nukkitx.com/job/GeyserMC/job/Geyser/job/master/lastStableBuild/artifact/bootstrap/spigot/target/Geyser-Spigot.jar' /mnt/dietpi_userdata/papermc/plugins/Geyser-Spigot.jar
				Download_Install 'https://ci.nukkitx.com/job/GeyserMC/job/Floodgate/job/master/lastStableBuild/artifact/bukkit/target/floodgate-bukkit.jar' /mnt/dietpi_userdata/papermc/plugins/floodgate-bukkit.jar
			fi

			# Minecraft rcon client for remote administration and server maintenance scripts
			Download_Install 'https://github.com/Tiiffi/mcrcon/archive/master.tar.gz'
			G_EXEC gcc -g0 -O3 mcrcon-master/mcrcon.c -o /usr/local/bin/mcrcon
			G_EXEC rm -R mcrcon-master
			G_EXEC strip --remove-section=.comment --remove-section=.note /usr/local/bin/mcrcon

			# Link logs to RAM
			G_EXEC mkdir -p /var/log/papermc
			G_EXEC rm -Rf /mnt/dietpi_userdata/papermc/logs
			G_EXEC ln -s /var/log/papermc /mnt/dietpi_userdata/papermc/logs

			# Permissions
			G_EXEC chown -R papermc:papermc /{mnt/dietpi_userdata,var/log}/papermc

			# Assure 1.5 GiB overall memory (-100 MiB to avoid tiny swap space) is available.
			local heap_size='512'
			if (( $RAM_TOTAL < 1436 ))
			then
				G_DIETPI-NOTIFY 2 'Stable PaperMC operation requires at least 1.5 GiB system memory. We will now increase your swap size to satisfy this requirement.'
				/boot/dietpi/func/dietpi-set_swapfile $(( 1536 - $RAM_PHYS ))

			# On 2 GiB+ physical RAM devices, apply 1 GiB heap size by default
			elif (( $RAM_PHYS > 1848 ))
			then
				heap_size='1024'
			fi

			# Service
			cat << _EOF_ > /etc/systemd/system/papermc.service
[Unit]
Description=papermc (DietPi)
Documentation=https://paper.readthedocs.io/

[Service]
User=papermc
SyslogIdentifier=papermc
WorkingDirectory=/mnt/dietpi_userdata/papermc
ExecStart=$(command -v java) -Xmx${heap_size}m -jar /opt/papermc/paperclip.jar --nogui --noconsole

[Install]
WantedBy=multi-user.target
_EOF_
			# Config
			if [[ -f '/mnt/dietpi_userdata/papermc/plugins/Geyser-Spigot.jar' && -f '/mnt/dietpi_userdata/papermc/plugins/floodgate-bukkit.jar' ]]
			then
				Create_Config /mnt/dietpi_userdata/papermc/plugins/Geyser-Spigot/config.yml papermc 1800 1 &&
				G_CONFIG_INJECT 'auth-type:[[:blank:]]' 'auth-type: floodgate' /mnt/dietpi_userdata/papermc/plugins/Geyser-Spigot/config.yml
			else
				Create_Config /mnt/dietpi_userdata/papermc/permissions.yml papermc 1800 1
			fi
			G_CONFIG_INJECT 'enable-rcon=' 'enable-rcon=true' /mnt/dietpi_userdata/papermc/server.properties
			GCI_PASSWORD=1 G_CONFIG_INJECT 'rcon.password=' "rcon.password=$GLOBAL_PW" /mnt/dietpi_userdata/papermc/server.properties
		fi

		software_id=100 # PiJuice
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			G_EXEC mkdir -p /var/lib/dietpi/dietpi-software/installed/pijuice
			echo -e '#!/bin/dash\npoweroff' > /var/lib/dietpi/dietpi-software/installed/pijuice/pijuice_func1.sh
			G_EXEC chmod +x /var/lib/dietpi/dietpi-software/installed/pijuice/pijuice_func1.sh

			cat << '_EOF_' > /var/lib/pijuice/pijuice_config.JSON
{
  "system_events": {
    "low_battery_voltage": {
      "function": "SYS_FUNC_HALT",
      "enabled": true
    },
    "low_charge": {
      "function": "NO_FUNC",
      "enabled": true
    },
    "button_power_off": {
      "function": "USER_FUNC1",
      "enabled": true
    },
    "forced_power_off": {
      "function": "USER_FUNC2",
      "enabled": false
    },
    "no_power": {
      "function": "SYS_FUNC_HALT_POW_OFF",
      "enabled": true
    },
    "forced_sys_power_off": {
      "function": "USER_FUNC3",
      "enabled": false
    },
    "watchdog_reset": {
      "function": "USER_EVENT",
      "enabled": true
    }
  },
  "user_functions": {
    "USER_FUNC1": "/var/lib/dietpi/dietpi-software/installed/pijuice/pijuice_func1.sh",
    "USER_FUNC2": "/var/lib/dietpi/dietpi-software/installed/pijuice/pijuice_func2.sh",
    "USER_FUNC3": "/var/lib/dietpi/dietpi-software/installed/pijuice/pijuice_func3.sh",
    "USER_FUNC4": "",
    "USER_FUNC5": "",
    "USER_FUNC6": "",
    "USER_FUNC7": "",
    "USER_FUNC8": ""
  },
  "system_task": {
    "watchdog": {
      "enabled": true,
      "period": "60"
    },
    "min_bat_voltage": {
      "threshold": "1",
      "enabled": true
    },
    "min_charge": {
      "threshold": "1",
      "enabled": true
    },
    "enabled": true,
    "wakeup_on_charge": {
      "enabled": true,
      "trigger_level": "1"
    }
  }
}
_EOF_
		fi

		software_id=178 # Jellyfin
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 )); then

			Banner_Configuration

			# Grant dietpi group permissions and assure video access
			Create_User -G dietpi,video,render -d /mnt/dietpi_userdata/jellyfin jellyfin

			# Config: Only apply on fresh install, assumed when /mnt/dietpi_userdata/jellyfin does not yet exist
			if [[ ! -d '/mnt/dietpi_userdata/jellyfin' ]]; then

				# Data dir
				# shellcheck disable=SC2015
				[[ -d '/var/lib/jellyfin' ]] && G_EXEC mv /var/lib/jellyfin /mnt/dietpi_userdata/jellyfin || G_EXEC mkdir -p /mnt/dietpi_userdata/jellyfin
				G_CONFIG_INJECT 'JELLYFIN_DATA_DIR=' 'JELLYFIN_DATA_DIR=/mnt/dietpi_userdata/jellyfin' /etc/default/jellyfin
				# Cache dir
				# shellcheck disable=SC2015
				[[ -d '/var/cache/jellyfin' ]] && G_EXEC mv /var/cache/jellyfin /mnt/dietpi_userdata/jellyfin/cache || G_EXEC mkdir -p /mnt/dietpi_userdata/jellyfin/cache
				G_CONFIG_INJECT 'JELLYFIN_CACHE_DIR=' 'JELLYFIN_CACHE_DIR=/mnt/dietpi_userdata/jellyfin/cache' /etc/default/jellyfin

			fi

			# Permissions
			G_EXEC chown -R jellyfin: /mnt/dietpi_userdata/jellyfin

			# RPi: Assure 128 MiB GPU memory on non-interactive installs, 320 MiB will be offered at the end of interactive installs: https://jellyfin.org/docs/general/administration/hardware-acceleration.html#raspberry-pi-3-and-4
			if (( $G_HW_MODEL < 10 )); then

				local gpu_mem=$(sed -n '/^[[:blank:]]*gpu_mem_1024=/{s/^[^=]*=//p;q}' /boot/config.txt)
				disable_error=1 G_CHECK_VALIDINT "$gpu_mem" 128 944 || /boot/dietpi/func/dietpi-set_hardware gpumemsplit 128

			fi

			Download_Test_Media

		fi

	}

	Install_Apply_GPU_Settings(){

		# Disable headless mode based on installed software
		local gpu_enabled=1

		# RPi: Define minimal memory split based on installed software
		local gpu_memory=0

		# Kodi, Jellyfin
		if (( ${aSOFTWARE_INSTALL_STATE[31]} == 1 ||
		      ${aSOFTWARE_INSTALL_STATE[178]} == 1 )); then

			gpu_memory=320
			(( ${G_HW_MEMORY_SIZE:-0} > 512 )) || gpu_memory=256

		# Descent
		elif (( ${aSOFTWARE_INSTALL_STATE[112]} == 1 )); then

			gpu_memory=192

		# RPi Cam Control, Amiberry, Chromium
		elif (( ${aSOFTWARE_INSTALL_STATE[59]} == 1 ||
			${aSOFTWARE_INSTALL_STATE[108]} == 1 ||
			${aSOFTWARE_INSTALL_STATE[113]} == 1 )); then

			gpu_memory=128

		# Desktops, OpenTyrian
		elif (( ${aSOFTWARE_INSTALL_STATE[23]} == 1 ||
			${aSOFTWARE_INSTALL_STATE[24]} == 1 ||
			${aSOFTWARE_INSTALL_STATE[25]} == 1 ||
			${aSOFTWARE_INSTALL_STATE[26]} == 1 ||
			${aSOFTWARE_INSTALL_STATE[51]} == 1 )); then

			gpu_memory=64

		fi

		# RPi: Apply memory split
		if (( $G_HW_MODEL < 10 && $gpu_memory > 0 )); then

			# Never override a higher existing value
			local current_gpu_mem=$(sed -n '/^[[:blank:]]*gpu_mem_1024=/{s/^[^=]*=//p;q}' /boot/config.txt)
			G_WHIP_DEFAULT_ITEM='Ok'
			if (( $current_gpu_mem < $gpu_memory )) && G_WHIP_YESNO "[ INFO ] DietPi has detected an increased GPU memory is required for your installed software:
 - Current: $current_gpu_mem MiB\n - Recommended: $gpu_memory MiB\n\nWould you like DietPi to apply the recommended GPU memory split?\n\nIf unsure, select 'Ok'."; then

				/boot/dietpi/func/dietpi-set_hardware gpumemsplit $gpu_memory

			fi

		fi

		# Disable headless/enable GPU
		(( $gpu_enabled )) && /boot/dietpi/func/dietpi-set_hardware headless 0

	}

	Check_USB_Drive_Installed(){

		USBDRIVE=0

		FP_DIETPI_DEDICATED_USBDRIVE=$(df -P | mawk '/^\/dev\/sda1/{print $6;exit}')

		# Only enable if mounted
		[[ $FP_DIETPI_DEDICATED_USBDRIVE ]] && df -P | grep -qi "$FP_DIETPI_DEDICATED_USBDRIVE" && USBDRIVE=1

	}

	Uninstall_Software(){

		# NB: systemctl daemon-reload is executed at the end of this function
		local software_id

		software_id=23 # LXDE
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			apt-mark auto upower policykit-1 firefox-esr dbus-user-session
			G_AGP lxde 'lxde-*'
			rm -Rf /{root,home/*}/.config/{lxpanel,lxsession,lxterminal}
			[[ -f '/etc/apt/preferences.d/dietpi-lxde' ]] && rm -v /etc/apt/preferences.d/dietpi-lxde

		fi

		software_id=173 # LXQt
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			apt-mark auto qterminal firefox-esr xarchiver lxde-icon-theme upower xscreensaver leafpad featherpad speedcrunch
			G_AGP lxqt
			rm -Rf /{root,home/*}/.config/lxqt

		fi

		software_id=174 # GIMP
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP gimp

		fi

		software_id=175 # Xfce Power Manager
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP xfce4-power-manager

		fi

		software_id=24 # MATE
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			apt-mark auto upower policykit-1 firefox-esr
			G_AGP mate-desktop-environment-core mate-media

		fi

		software_id=26 # GNUstep
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			apt-mark auto upower policykit-1 firefox-esr
			G_AGP x-window-system-core wmaker gnustep gnustep-devel gnustep-games

		fi

		software_id=25 # Xfce
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			apt-mark auto gnome-icon-theme tango-icon-theme upower policykit-1 firefox-esr
			G_AGP xfce4

		fi

		software_id=22 # QuiteRSS
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP quiterss

		fi

		software_id=30 # NoMachine
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP nomachine

		fi

		software_id=29 # XRDP
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP xrdp xorgxrdp
			[[ -f '/etc/apt/preferences.d/dietpi-xrdp' ]] && rm -v /etc/apt/preferences.d/dietpi-xrdp

		fi

		software_id=44 # Transmission
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP transmission-daemon
			getent passwd debian-transmission > /dev/null && userdel debian-transmission
			getent group debian-transmission > /dev/null && groupdel debian-transmission
			[[ -f '/etc/systemd/system/transmission-daemon.service' ]] && rm /etc/systemd/system/transmission-daemon.service # pre v6.17
			[[ -d '/etc/systemd/system/transmission-daemon.service.d' ]] && rm -R /etc/systemd/system/transmission-daemon.service.d # post v6.17

		fi

		software_id=47 # ownCloud
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			# Remove background cron job
			crontab -u www-data -l | grep -v '/var/www/owncloud/.*cron' | crontab -u www-data -
			# Disable and remove PHP modules
			phpdismod dietpi-owncloud &> /dev/null
			rm -f /etc/php/*/mods-available/dietpi-owncloud.ini
			# Disable and remove webserver configs
			a2dissite dietpi-owncloud 2> /dev/null
			rm /etc/apache2/sites-available/dietpi-owncloud.conf 2> /dev/null
			rm /etc/nginx/sites-dietpi/dietpi-owncloud.conf 2> /dev/null
			lighty-disable-mod dietpi-owncloud 2> /dev/null
			rm /etc/lighttpd/conf-available/99-dietpi-owncloud.conf 2> /dev/null
			G_WHIP_MSG "DietPi will perform an automated backup of your ownCloud database and installation directory, which will be stored inside your ownCloud data directory.\n\nThe data directory won't be removed. So you can recover your whole ownCloud instance any time later.\n\nRemove the data directory manually, if you don't need it anymore."
			# Drop MariaDB users and database
			local dbuser=$(grep -m1 "^[[:blank:]]*'dbuser'" /var/www/owncloud/config/config.php | mawk '{print $3}' | sed 's/,//')
			local dbhost=$(grep -m1 "^[[:blank:]]*'dbhost'" /var/www/owncloud/config/config.php | mawk '{print $3}' | sed 's/,//')
			# - Find datadir for backups
			local datadir=$(grep -m1 "^[[:blank:]]*'datadirectory'" /var/www/owncloud/config/config.php | mawk '{print $3}' | sed "s/[',]//g")
			[[ $datadir ]] || datadir=/mnt/dietpi_userdata/owncloud_data
			systemctl start mariadb
			mysql -e "drop user $dbuser@$dbhost"
			mysql -e "drop user $dbuser" 2> /dev/null
			# - Perform database backup if existent, otherwise skip to not overwrite existing one
			[[ -d '/mnt/dietpi_userdata/mysql/owncloud' ]] && mysqldump owncloud > $datadir/dietpi-owncloud-database-backup.sql
			mysqladmin drop owncloud -f
			# Backup ownCloud installation folder
			cp -a /var/www/owncloud/. $datadir/dietpi-owncloud-installation-backup/
			# Remove ownCloud installation folder
			rm -R /var/www/owncloud
			# Remove redirect configs
			if grep -q 'owncloud' /etc/lighttpd/conf-available/99-dietpi-dav_redirect.conf &> /dev/null; then

				lighty-disable-mod dietpi-dav_redirect
				rm /etc/lighttpd/conf-available/99-dietpi-dav_redirect.conf

			fi
			if grep -q 'owncloud' /etc/apache2/conf-available/dietpi-dav_redirect.conf &> /dev/null; then

				a2disconf dietpi-dav_redirect
				rm /etc/apache2/conf-available/dietpi-dav_redirect.conf

			fi
			grep -q 'owncloud' /etc/nginx/sites-dietpi/dietpi-dav_redirect.conf &> /dev/null && rm /etc/nginx/sites-dietpi/dietpi-dav_redirect.conf

		fi

		software_id=168 # Nextcloud Talk + coTURN server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP coturn
			[[ -f '/etc/turnserver.conf' ]] && rm /etc/turnserver.conf
			[[ -d '/etc/systemd/system/coturn.service.d' ]] && rm -R /etc/systemd/system/coturn.service.d
			systemctl start mariadb
			systemctl start redis-server
			ncc maintenance:mode --off
			ncc app:disable spreed
			G_DIETPI-NOTIFY 2 'Disabled Nextcloud Talk app, but you need to remove it manually from Nextcloud web UI, if desired.'

		fi

		software_id=114 # Nextcloud
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			crontab -u www-data -l | grep -v '/var/www/nextcloud/cron.php' | crontab -u www-data -
			# Disable and remove PHP modules
			phpdismod dietpi-nextcloud &> /dev/null
			rm -f /etc/php/*/mods-available/dietpi-nextcloud.ini
			# Disable and remove webserver configs
			a2dissite dietpi-nextcloud 2> /dev/null
			rm /etc/apache2/sites-available/dietpi-nextcloud.conf 2> /dev/null
			rm /etc/nginx/sites-dietpi/dietpi-nextcloud.conf 2> /dev/null
			lighty-disable-mod dietpi-nextcloud 2> /dev/null
			rm /etc/lighttpd/conf-available/99-dietpi-nextcloud.conf 2> /dev/null
			G_WHIP_MSG "DietPi will perform an automated backup of your Nextcloud database and installation directory, which will be stored inside your Nextcloud data directory.\n\nThe data directory won't be removed. So you can recover your whole Nextcloud instance any time later.\n\nRemove the data directory manually, if you don't need it anymore."
			# Drop MariaDB users and database
			local dbuser=$(grep -m1 "^[[:blank:]]*'dbuser'" /var/www/nextcloud/config/config.php | mawk '{print $3}' | sed 's/,//')
			local dbhost=$(grep -m1 "^[[:blank:]]*'dbhost'" /var/www/nextcloud/config/config.php | mawk '{print $3}' | sed 's/,//')
			# - Find datadir for backups
			local datadir=$(grep -m1 "^[[:blank:]]*'datadirectory'" /var/www/nextcloud/config/config.php | mawk '{print $3}' | sed "s/[',]//g")
			[[ $datadir ]] || datadir=/mnt/dietpi_userdata/nextcloud_data
			systemctl start mariadb
			mysql -e "drop user $dbuser@$dbhost"
			mysql -e "drop user $dbuser" 2> /dev/null
			# - Perform database backup if existent, otherwise skip to not overwrite existing one
			[[ -d '/mnt/dietpi_userdata/mysql/nextcloud' ]] && mysqldump nextcloud > $datadir/dietpi-nextcloud-database-backup.sql
			mysqladmin drop nextcloud -f
			# Backup Nextcloud installation folder
			cp -a /var/www/nextcloud/. $datadir/dietpi-nextcloud-installation-backup/
			# Remove Nextcloud installation folder
			rm -R /var/www/nextcloud
			# Remove redirect configs
			if grep -q 'nextcloud' /etc/lighttpd/conf-available/99-dietpi-dav_redirect.conf &> /dev/null; then

				lighty-disable-mod dietpi-dav_redirect
				rm /etc/lighttpd/conf-available/99-dietpi-dav_redirect.conf

			fi
			if grep -q 'nextcloud' /etc/apache2/conf-available/dietpi-dav_redirect.conf &> /dev/null; then

				a2disconf dietpi-dav_redirect
				rm /etc/apache2/conf-available/dietpi-dav_redirect.conf

			fi
			grep -q 'nextcloud' /etc/nginx/sites-dietpi/dietpi-dav_redirect.conf &> /dev/null && rm /etc/nginx/sites-dietpi/dietpi-dav_redirect.conf

		fi

		software_id=63 # LinuxDash
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			[[ -d '/var/www/linuxdash' ]] && rm -R /var/www/linuxdash

		fi

		software_id=62 # Box86
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 ))
		then
			Banner_Uninstalling
			[[ -f '/usr/local/bin/box86' ]] && rm -v /usr/local/bin/box86
			[[ -f '/etc/binfmt.d/box86.conf' ]] && rm -v /etc/binfmt.d/box86.conf
			[[ -f '/usr/lib/i386-linux-gnu/libstdc++.so.6' ]] && rm -v /usr/lib/i386-linux-gnu/libstdc++.so.6
			[[ -f '/usr/lib/i386-linux-gnu/libstdc++.so.5' ]] && rm -v /usr/lib/i386-linux-gnu/libstdc++.so.5
			[[ -f '/usr/lib/i386-linux-gnu/libgcc_s.so.1' ]] && rm -v /usr/lib/i386-linux-gnu/libgcc_s.so.1
			[[ -d '/usr/lib/i386-linux-gnu' ]] && rmdir --ignore-fail-on-non-empty /usr/lib/i386-linux-gnu
		fi

		software_id=27 # TasmoAdmin
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			[[ -d '/var/www/tasmoadmin' ]] && rm -R /var/www/tasmoadmin

			# Remove webserver configs
			command -v a2dissite > /dev/null && a2dissite dietpi-tasmoadmin
			[[ -f '/etc/apache2/conf-available/dietpi-tasmoadmin.conf' ]] && rm /etc/apache2/conf-available/dietpi-tasmoadmin.conf
			[[ -f '/etc/nginx/sites-dietpi/dietpi-tasmoadmin.conf' ]] && rm /etc/nginx/sites-dietpi/dietpi-tasmoadmin.conf
			command -v lighty-disable-mod > /dev/null && lighty-disable-mod dietpi-tasmoadmin
			[[ -f '/etc/lighttpd/conf-available/99-dietpi-tasmoadmin.conf' ]] && rm /etc/lighttpd/conf-available/99-dietpi-tasmoadmin.conf

		fi

		software_id=83 # Apache2
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP apache2

		fi

		software_id=85 # Nginx
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP nginx nginx-*

		fi

		software_id=84 # Lighttpd
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP lighttpd

			# Remove certbot renewal hook
			[[ -f 'etc/systemd/system/certbot.service.d/dietpi-lighttpd.conf' ]] && rm /etc/systemd/system/certbot.service.d/dietpi-lighttpd.conf
			# Remove hook directory if empty
			[[ -d '/etc/systemd/system/certbot.service.d' ]] && rmdir --ignore-fail-on-non-empty /etc/systemd/system/certbot.service.d

		fi

		software_id=87 # SQLite
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP sqlite3 php*-sqlite3

		fi

		software_id=91 # Redis
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP redis-server redis-tools 'php*-redis'

		fi

		software_id=89 # PHP
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP php*-* libapache2-mod-php*
			[[ -d '/etc/php' ]] && rm -R /etc/php
			[[ -f '/var/www/phpinfo.php' ]] && rm /var/www/phpinfo.php
			[[ -f '/var/www/apc.php' ]] && rm /var/www/apc.php
			[[ -f '/var/www/opcache.php' ]] && rm /var/www/opcache.php
			# temp php uploads, if it was created
			[[ -d '/var/tmp/php_upload_tmp' ]] && rm -R /var/tmp/php_upload_tmp

			## Leave PHP repo in place to avoid issues with remaining dependency packages that were pulled from this repo
			# APT repo
			#[[ -f '/etc/apt/sources.list.d/dietpi-php.list' ]] && rm /etc/apt/sources.list.d/dietpi-php.list
			# APT preferences
			#[[ -f '/etc/apt/preferences.d/dietpi-openssl' ]] && rm /etc/apt/preferences.d/dietpi-openssl
			#[[ -f '/etc/apt/preferences.d/dietpi-php' ]] && rm /etc/apt/preferences.d/dietpi-php

		fi

		software_id=90 # phpMyAdmin
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			systemctl start mariadb
			mysqladmin -f drop phpmyadmin
			mysql -e 'drop user phpmyadmin@localhost'

		fi

		software_id=184 # Tor Relay
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 ))
		then
			Banner_Uninstalling
			if [[ -f '/lib/systemd/system/tor.service' ]]
			then
				systemctl unmask tor
				systemctl disable --now tor
				rm -R /lib/systemd/system/tor.service*
			fi
			[[ -d '/etc/systemd/system/tor.service.d' ]] && rm -R /etc/systemd/system/tor.service.d
			G_AGP tor obfs4proxy
		fi

		software_id=54 # phpBB
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			systemctl start mariadb
			mysqladmin -f drop phpbb drop phpbb3 # phpbb3: Pre-v6.33
			mysql -e 'drop user phpbb@localhost; drop user phpbb3@localhost' # phpbb3: Pre-v6.33
			[[ -d '/var/www/phpbb' ]] && rm -R /var/www/phpbb
			[[ -d '/var/www/phpBB3' ]] && rm -R /var/www/phpBB3 # Pre-v6.33

		fi

		software_id=115 # Webmin
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/webmin.service' ]]; then

				systemctl disable --now webmin
				rm -R /etc/systemd/system/webmin.service*

			fi
			[[ -d '/etc/systemd/system/webmin.service.d' ]] && rm -R /etc/systemd/system/webmin.service.d
			G_AGP webmin
			[[ -f '/etc/apt/sources.list.d/webmin.list' ]] && rm -v /etc/apt/sources.list.d/webmin.list
			[[ -f '/etc/apt/trusted.gpg.d/dietpi-webmin.gpg' ]] && rm -v /etc/apt/trusted.gpg.d/dietpi-webmin.gpg

		fi

		software_id=32 # ympd
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/ympd.service' ]]; then

				systemctl disable --now ympd
				rm -R /etc/systemd/system/ympd.service*

			fi
			[[ -d '/etc/systemd/system/ympd.service.d' ]] && rm -R /etc/systemd/system/ympd.service.d
			getent passwd ympd > /dev/null && userdel ympd
			getent group ympd > /dev/null && groupdel ympd # pre-v6.33
			[[ -f '/usr/bin/ympd' ]] && rm /usr/bin/ympd

		fi

		software_id=148 # myMPD
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			if [[ -f '/lib/systemd/system/mympd.service' ]]; then

				systemctl unmask mympd
				systemctl disable --now mympd
				rm /lib/systemd/system/mympd.service

			fi
			[[ -d '/etc/systemd/system/mympd.service.d' ]] && rm -R /etc/systemd/system/mympd.service.d
			getent passwd mympd > /dev/null && userdel mympd
			getent group mympd > /dev/null && groupdel mympd # pre-v6.29
			command -v mympd > /dev/null && rm "$(command -v mympd)"
			rm -Rf /{etc,var/lib,usr/share}/mympd /etc/mympd.conf* /etc/systemd/system/mympd.service.d /usr/share/man/man1/mympd.*

		fi

		software_id=128 # MPD
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/lib/systemd/system/mpd.service' ]]; then

				systemctl unmask mpd
				systemctl disable --now mpd
				rm -R /lib/systemd/system/mpd.service*

			fi
			[[ -d '/etc/systemd/system/mpd.service.d' ]] && rm -R /etc/systemd/system/mpd.service.d
			[[ -f '/lib/systemd/system/mpd.socket' ]] && rm -v /lib/systemd/system/mpd.socket
			getent passwd mpd > /dev/null && userdel mpd
			getent group mpd > /dev/null && groupdel mpd # pre-v6.29
			G_AGP mpd libmpdclient2
			[[ -d '/var/log/mpd' ]] && rm -R /var/log/mpd
			[[ -d '/mnt/dietpi_userdata/.mpd_cache' ]] && rm -R /mnt/dietpi_userdata/.mpd_cache
			[[ -f '/etc/mpd.conf' ]] && rm -v /etc/mpd.conf
			[[ -f '/usr/local/etc/mpd.conf' ]] && rm /usr/local/etc/mpd.conf && rmdir --ignore-fail-on-non-empty /usr/local/etc # Pre-v6.29
			[[ -f '/etc/default/mpd' ]] && rm -v /etc/default/mpd # pre-v6.20

		fi

		software_id=122 # Node-RED
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling

			# Service
			if [[ -f '/etc/systemd/system/node-red.service' ]]; then

				systemctl disable --now node-red
				rm -R /etc/systemd/system/node-red.service*

			fi
			[[ -d '/etc/systemd/system/node-red.service.d' ]] && rm -R /etc/systemd/system/node-red.service.d

			# User
			getent passwd nodered > /dev/null && userdel nodered
			getent group nodered > /dev/null && groupdel nodered

			# Configs
			[[ -f '/etc/sudoers.d/nodered' ]] && G_EXEC_NOEXIT=1 G_EXEC rm /etc/sudoers.d/nodered
			[[ -f '/etc/bashrc.d/dietpi-node-red.sh' ]] && G_EXEC_NOEXIT=1 G_EXEC rm /etc/bashrc.d/dietpi-node-red.sh

			# Data
			[[ -d '/mnt/dietpi_userdata/node-red' ]] && G_EXEC_NOEXIT=1 G_EXEC rm -R /mnt/dietpi_userdata/node-red
			G_EXEC_NOEXIT=1 G_EXEC rm -Rf /{root,home/*}/.node-red # Pre-v6.25

			# Pre-v7.0
			npm r -g --unsafe-perm node-red
			[[ -f '/usr/local/bin/node-red' ]] && G_EXEC_NOEXIT=1 G_EXEC rm /usr/local/bin/node-red
			[[ -f '/usr/local/bin/node-red-pi' ]] && G_EXEC_NOEXIT=1 G_EXEC rm /usr/local/bin/node-red-pi

		fi

		software_id=123 # Mosquitto
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/mosquitto.service' ]]; then

				systemctl disable --now mosquitto
				rm -R /etc/systemd/system/mosquitto.service*

			fi
			[[ -d '/etc/systemd/system/mosquitto.service.d' ]] && rm -R /etc/systemd/system/mosquitto.service.d
			G_AGP mosquitto

			[[ -f '/etc/apt/sources.list.d/dietpi-mosquitto.list' ]] && rm -v /etc/apt/sources.list.d/dietpi-mosquitto.list
			[[ -f '/etc/apt/trusted.gpg.d/dietpi-mosquitto.gpg' ]] && rm -v /etc/apt/trusted.gpg.d/dietpi-mosquitto.gpg

		fi

		software_id=124 # NAA Daemon
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP networkaudiod

		fi

		software_id=125 # Tomcat8
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP tomcat8

		fi

		software_id=126 # LibSSL1.0.0
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			apt-mark auto libssl1.0.0 multiarch-support

		fi

		software_id=129 # O!MPD
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			[[ -d '/var/www/ompd' ]] && rm -R /var/www/ompd
			if [[ -f '/etc/lighttpd/conf-available/99-dietpi-ompd.conf' ]]; then

				lighty-disable-mod dietpi-ompd
				rm /etc/lighttpd/conf-available/99-dietpi-ompd.conf

			fi
			systemctl start mariadb
			mysqladmin drop ompd -f
			mysql -e 'drop user ompd@localhost'

		fi

		software_id=131 # Blynk Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/blynkserver.service' ]]; then

				systemctl disable --now blynkserver
				rm -R /etc/systemd/system/blynkserver.service*

			fi
			[[ -d '/etc/systemd/system/blynkserver.service.d' ]] && rm -R /etc/systemd/system/blynkserver.service.d
			getent passwd blynk > /dev/null && userdel blynk
			getent group blynk > /dev/null && groupdel blynk # Pre-v6.33
			[[ -d '/mnt/dietpi_userdata/blynk' ]] && rm -R /mnt/dietpi_userdata/blynk
			[[ -d '/var/log/blynk' ]] && rm -R /var/log/blynk
			[[ -d '/etc/blynkserver' ]] && rm -R /etc/blynkserver # Pre-v6.19

		fi

		software_id=132
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			[[ -d '/var/www/aria2' ]] && rm -R /var/www/aria2
			if [[ -f '/etc/systemd/system/aria2.service' ]]; then

				systemctl disable --now aria2
				rm -R /etc/systemd/system/aria2.service*

			fi
			[[ -d '/etc/systemd/system/aria2.service.d' ]] && rm -R /etc/systemd/system/aria2.service.d
			G_AGP aria2
			getent passwd aria2 > /dev/null && userdel aria2
			getent group aria2 > /dev/null && groupdel aria2
			[[ -d '/mnt/dietpi_userdata/aria2' ]] && rm -R /mnt/dietpi_userdata/aria2
			[[ -f '/mnt/dietpi_userdata/downloads/aria2.session' ]] && rm /mnt/dietpi_userdata/downloads/aria2.session
			[[ -f '/usr/local/bin/aria2c' ]] && rm /usr/local/bin/aria2c # Previous

		fi

		software_id=133 # YaCy
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/yacy.service' ]]; then

				systemctl disable --now yacy
				rm -R /etc/systemd/system/yacy.service*

			fi
			[[ -d '/etc/systemd/system/yacy.service.d' ]] && rm -R /etc/systemd/system/yacy.service.d
			rm -R /etc/yacy

		fi

		software_id=2 # Folding@Home
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			# Un-check out all work units, so they can be picked up by other donors prior to timeout: https://github.com/FoldingAtHome/fah-issues/issues/1255
			FAHClient --chdir /mnt/dietpi_userdata/fahclient --dump all

			if [[ -f '/etc/systemd/system/fahclient.service' ]]; then

				systemctl disable --now fahclient
				rm -R /etc/systemd/system/fahclient.service*

			fi
			[[ -d '/etc/systemd/system/fahclient.service.d' ]] && rm -R /etc/systemd/system/fahclient.service.d
			G_AGP fahclient
			[[ -d '/mnt/dietpi_userdata/fahclient' ]] && rm -R /mnt/dietpi_userdata/fahclient

		fi

		software_id=135
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/darkice.service' ]]; then

				systemctl disable --now darkice
				rm -R /etc/systemd/system/darkice.service*

			fi
			[[ -d '/etc/systemd/system/darkice.service.d' ]] && rm -R /etc/systemd/system/darkice.service.d
			G_AGP darkice icecast2

		fi

		software_id=136 # MotionEye
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/motioneye.service' ]]; then

				systemctl disable --now motioneye
				rm -R /etc/systemd/system/motioneye.service*

			fi
			[[ -d '/etc/systemd/system/motioneye.service.d' ]] && rm -R /etc/systemd/system/motioneye.service.d
			pip2 uninstall -y motioneye
			G_AGP motion
			[[ -d '/etc/motioneye' ]] && rm -R /etc/motioneye

		fi

		software_id=137 # mjpg-streamer
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 ))
		then
			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/mjpg-streamer.service' ]]
			then
				systemctl disable --now mjpg-streamer
				rm -R /etc/systemd/system/mjpg-streamer.service*
			fi
			[[ -d '/etc/systemd/system/mjpg-streamer.service.d' ]] && rm -R /etc/systemd/system/mjpg-streamer.service.d
			getent passwd mjpg-streamer > /dev/null && userdel mjpg-streamer
			[[ -d '/opt/mjpg-streamer' ]] && G_EXEC_NOEXIT=1 G_EXEC rm -R /opt/mjpg-streamer
		fi

		software_id=138 # VirtualHere
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/virtualhere.service' ]]; then

				systemctl disable --now virtualhere
				rm -R /etc/systemd/system/virtualhere.service*

			fi
			[[ -d '/etc/systemd/system/virtualhere.service.d' ]] && rm -R /etc/systemd/system/virtualhere.service.d
			[[ -d '/etc/vhusbd' ]] && rm -R /etc/vhusbd

		fi

		software_id=139 # SABnzbd
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/sabnzbd.service' ]]; then

				systemctl disable --now sabnzbd
				rm -R /etc/systemd/system/sabnzbd.service*

			fi
			[[ -d '/etc/systemd/system/sabnzbd.service.d' ]] && rm -R /etc/systemd/system/sabnzbd.service.d
			getent passwd sabnzbd > /dev/null && userdel sabnzbd
			getent group sabnzbd > /dev/null && groupdel sabnzbd # Pre-v6.33
			[[ -d '/etc/sabnzbd' ]] && rm -R /etc/sabnzbd
			[[ -d '/var/log/sabnzbd' ]] && rm -R /var/log/sabnzbd

		fi

		software_id=177 # Firefox Sync Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/firefox-sync.service' ]]; then

				systemctl disable --now firefox-sync
				rm -R /etc/systemd/system/firefox-sync.service*

			fi
			[[ -d '/etc/systemd/system/firefox-sync.service.d' ]] && rm -R /etc/systemd/system/firefox-sync.service.d
			getent passwd ffsync > /dev/null && userdel ffsync
			getent group ffsync > /dev/null && groupdel ffsync
			[[ -d '/opt/firefox-sync' ]] && rm -R /opt/firefox-sync
			[[ -d '/mnt/dietpi_userdata/firefox-sync' ]] && rm -R /mnt/dietpi_userdata/firefox-sync

		fi

		software_id=183 # Bitwarden_RS
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling

			if [[ -f '/etc/systemd/system/bitwarden_rs.service' ]]; then

				systemctl disable --now bitwarden_rs
				rm -R /etc/systemd/system/bitwarden_rs.service*

			fi
			[[ -d '/etc/systemd/system/bitwarden_rs.service.d' ]] && rm -R /etc/systemd/system/bitwarden_rs.service.d

			getent passwd bitwarden_rs > /dev/null && userdel bitwarden_rs
			getent group bitwarden_rs > /dev/null && groupdel bitwarden_rs

			[[ -d '/opt/bitwarden_rs' ]] && rm -R /opt/bitwarden_rs
			[[ -d '/mnt/dietpi_userdata/bitwarden_rs' ]] && rm -R /mnt/dietpi_userdata/bitwarden_rs

		fi

		software_id=141
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/spotify-connect-web.service' ]]; then

				systemctl disable --now spotify-connect-web
				rm -R /etc/systemd/system/spotify-connect-web.service*

			fi
			[[ -d '/etc/systemd/system/spotify-connect-web.service.d' ]] && rm -R /etc/systemd/system/spotify-connect-web.service.d
			[[ -d '/mnt/dietpi_userdata/spotify-connect-web' ]] && rm -R /mnt/dietpi_userdata/spotify-connect-web

		fi

		software_id=182 # Unbound
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 ))
		then
			Banner_Uninstalling

			# Pi-hole: Assure that it does not resolve via Unbound anymore
			if [[ -f '/etc/dnsmasq.d/01-pihole.conf' ]] && grep -q '^[[:blank:]]*server=127.0.0.1' /etc/dnsmasq.d/01-pihole.conf
			then
				G_WHIP_MSG 'The Pi-hole upstream DNS server has been changed to Quad9 due to Unbound being uninstalled.'
				G_CONFIG_INJECT 'server=127.0.0.1' 'server=9.9.9.9' /etc/dnsmasq.d/01-pihole.conf
				systemctl -q is-active pihole-FTL && G_EXEC systemctl restart pihole-FTL
			fi
			[[ -f '/etc/pihole/setupVars.conf' ]] && grep -q '^[[:blank:]]*PIHOLE_DNS_1=127.0.0.1' /etc/pihole/setupVars.conf && G_CONFIG_INJECT 'PIHOLE_DNS_1=' 'PIHOLE_DNS_1=9.9.9.9' /etc/pihole/setupVars.conf

			G_AGP unbound
			[[ -d '/etc/unbound' ]] && rm -R /etc/unbound
		fi

		software_id=142 # CouchPotato
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/init.d/couchpotato' ]]; then

				systemctl unmask couchpotato
				systemctl disable --now couchpotato
				rm /etc/init.d/couchpotato
				update-rc.d -f couchpotato remove

			fi
			[[ -d '/etc/systemd/system/couchpotato.service.d' ]] && rm -R /etc/systemd/system/couchpotato.service.d
			getent passwd couchpotato > /dev/null && userdel couchpotato # Currently not created
			getent group couchpotato > /dev/null && groupdel couchpotato # Currently not created
			[[ -d '/etc/couchpotato' ]] && rm -R /etc/couchpotato
			[[ -d '/mnt/dietpi_userdata/couchpotato' ]] && rm -R /mnt/dietpi_userdata/couchpotato

		fi

		software_id=143 # Koel
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling

			if [[ -f '/etc/systemd/system/koel.service' ]]; then

				systemctl disable --now koel
				rm -R /etc/systemd/system/koel.service*

			fi
			[[ -d '/etc/systemd/system/koel.service.d' ]] && rm -R /etc/systemd/system/koel.service.d
			getent passwd koel > /dev/null && userdel koel
			getent group koel > /dev/null && groupdel koel # Pre-v6.33

			systemctl start mariadb
			mysqladmin drop koel -f
			mysql -e 'drop user koel@localhost'

			[[ -d '/mnt/dietpi_userdata/koel' ]] && rm -R /mnt/dietpi_userdata/koel

		fi

		software_id=144 # Sonarr
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/sonarr.service' ]]; then

				systemctl disable --now sonarr
				rm -R /etc/systemd/system/sonarr.service*

			fi
			[[ -d '/etc/systemd/system/sonarr.service.d' ]] && rm -R /etc/systemd/system/sonarr.service.d
			G_AGP nzbdrone
			getent passwd sonarr > /dev/null && userdel sonarr
			getent group sonarr > /dev/null && groupdel sonarr # Pre-v6.29
			rm -Rf /opt/NzbDrone /mnt/dietpi_userdata/sonarr /var/log/sonarr /etc/apt/sources.list.d/sonarr.list

		fi

		software_id=145 # Radarr
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/radarr.service' ]]; then

				systemctl disable --now radarr
				rm -R /etc/systemd/system/radarr.service*

			fi
			[[ -d '/etc/systemd/system/radarr.service.d' ]] && rm -R /etc/systemd/system/radarr.service.d
			getent passwd radarr > /dev/null && userdel radarr
			getent group radarr > /dev/null && groupdel radarr # Pre-v6.29
			rm -Rf /opt/radarr /mnt/dietpi_userdata/radarr /var/log/radarr

		fi

		software_id=106 # Lidarr
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/lidarr.service' ]]; then

				systemctl disable --now lidarr
				rm -R /etc/systemd/system/lidarr.service*

			fi
			[[ -d '/etc/systemd/system/lidarr.service.d' ]] && rm -R /etc/systemd/system/lidarr.service.d
			getent passwd lidarr > /dev/null && userdel lidarr
			getent group lidarr > /dev/null && groupdel lidarr # Pre-v6.29
			rm -Rf /opt/Lidarr /mnt/dietpi_userdata/lidarr /var/log/lidarr

		fi

		software_id=180 # Bazarr
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/bazarr.service' ]]; then

				systemctl disable --now bazarr
				rm -R /etc/systemd/system/bazarr.service*

			fi
			[[ -d '/etc/systemd/system/bazarr.service.d' ]] && rm -R /etc/systemd/system/bazarr.service.d
			getent passwd bazarr > /dev/null && userdel bazarr
			rm -Rf /opt/bazarr /mnt/dietpi_userdata/bazarr /var/log/bazarr

		fi

		software_id=146 # Tautulli
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/tautulli.service' ]]; then

				systemctl disable --now tautulli
				rm -R /etc/systemd/system/tautulli.service*

			fi
			[[ -d '/etc/systemd/system/tautulli.service.d' ]] && rm -R /etc/systemd/system/tautulli.service.d
			getent passwd tautulli > /dev/null && userdel tautulli
			getent group tautulli > /dev/null && groupdel tautulli
			[[ -d '/opt/tautulli' ]] && rm -R /opt/tautulli
			[[ -d '/mnt/dietpi_userdata/tautulli' ]] && rm -R /mnt/dietpi_userdata/tautulli

		fi

		software_id=147 # Jackett
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/jackett.service' ]]; then

				systemctl disable --now jackett
				rm -R /etc/systemd/system/jackett.service*

			fi
			[[ -d '/etc/systemd/system/jackett.service.d' ]] && rm -R /etc/systemd/system/jackett.service.d
			getent passwd jackett > /dev/null && userdel jackett
			getent group jackett > /dev/null && groupdel jackett
			[[ -d '/opt/jackett' ]] && rm -R /opt/jackett

		fi

		software_id=149 # NZBGet
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/nzbget.service' ]]; then

				systemctl disable --now nzbget
				rm -R /etc/systemd/system/nzbget.service*

			fi
			[[ -d '/etc/systemd/system/nzbget.service.d' ]] && rm -R /etc/systemd/system/nzbget.service.d
			getent passwd nzbget > /dev/null && userdel nzbget
			getent group nzbget > /dev/null && groupdel nzbget # Pre-v6.33
			[[ -d '/mnt/dietpi_userdata/nzbget' ]] && rm -R /mnt/dietpi_userdata/nzbget

		fi

		software_id=155 # HTPC Manager
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/htpc-manager.service' ]]; then

				systemctl disable --now htpc-manager
				rm -R /etc/systemd/system/htpc-manager.service*

			fi
			[[ -d '/etc/systemd/system/htpc-manager.service.d' ]] && rm -R /etc/systemd/system/htpc-manager.service.d
			[[ -d '/mnt/dietpi_userdata/htpc-manager' ]] && rm -R /mnt/dietpi_userdata/htpc-manager

		fi

		software_id=150 # Mono
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			apt-mark auto mono-complete mono-devel mono-runtime libmono-cil-dev 2>/dev/null
			[[ -f '/etc/apt/sources.list.d/mono-xamarin.list' ]] && rm /etc/apt/sources.list.d/mono-xamarin.list

		fi

		software_id=151 # Nvidia driver
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP nvidia-driver nvidia-xconfig libgl1-nvidia-glx:i386 nvidia-driver-libs-i386

		fi

		software_id=37 # Shairport-Sync
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP shairport-sync
			if [[ -f '/lib/systemd/system/shairport-sync.service' ]]; then

				systemctl unmask shairport-sync
				systemctl disable --now shairport-sync
				rm -Rf /{lib,etc}/systemd/system/shairport-sync*

			fi
			[[ -d '/etc/systemd/system/shairport-sync.service.d' ]] && rm -R /etc/systemd/system/shairport-sync.service.d
			getent passwd shairport-sync > /dev/null && userdel shairport-sync
			getent group shairport-sync > /dev/null && groupdel shairport-sync
			# Pre-v6.29
			rm -f /usr/local/bin/shairport-sync /usr/local/etc/shairport-sync.conf* /usr/local/share/man/man7/shairport-sync.7.gz

		fi

		software_id=152 # Avahi-Daemon
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP avahi-daemon

		fi

		software_id=153 # OctoPrint
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 ))
		then
			Banner_Uninstalling
			# Service
			if [[ -f '/etc/systemd/system/octoprint.service' ]]
			then
				systemctl disable --now octoprint
				rm -R /etc/systemd/system/octoprint.service*
			fi
			[[ -d '/etc/systemd/system/octoprint.service.d' ]] && rm -R /etc/systemd/system/octoprint.service.d
			# CLI alias
			[[ -f '/etc/bashrc.d/dietpi-octoprint.sh' ]] && rm -v /etc/bashrc.d/dietpi-octoprint.sh
			# Data
			[[ -d '/mnt/dietpi_userdata/octoprint' ]] && rm -R /mnt/dietpi_userdata/octoprint
			# sudoers
			[[ -f '/etc/sudoers.d/octoprint' ]] && rm -v /etc/sudoers.d/octoprint
			# User
			getent passwd octoprint > /dev/null && userdel octoprint
			getent group octoprint > /dev/null && groupdel octoprint
		fi

		software_id=121 # Roon Bridge
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/roonbridge.service' ]]; then

				systemctl disable --now roonbridge
				rm -R /etc/systemd/system/roonbridge.service*

			fi
			[[ -d '/etc/systemd/system/roonbridge.service.d' ]] && rm -R /etc/systemd/system/roonbridge.service.d
			[[ -d '/etc/roonbridge' ]] && rm -R /etc/roonbridge
			#getent passwd roon > /dev/null && userdel roon
			#getent group roon > /dev/null && groupdel roon

		fi

		software_id=154 # Roon Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/roonserver.service' ]]; then

				systemctl disable --now roonserver
				rm -R /etc/systemd/system/roonserver.service*

			fi
			[[ -d '/etc/systemd/system/roonserver.service.d' ]] && rm -R /etc/systemd/system/roonserver.service.d
			[[ -d '/mnt/dietpi_userdata/roonserver' ]] && rm -R /mnt/dietpi_userdata/roonserver

		fi

		software_id=156 # Steam
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 ))
		then
			Banner_Uninstalling
			G_AGP steam
			rm -Rf /{root,home/*}/{.steam{,path,pid},Desktop/steam.desktop} /mnt/dietpi_userdata/steam
		fi

		software_id=119 # CAVA
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 ))
		then
			Banner_Uninstalling
			G_AGP cava
			rm -Rf /{root,home/*}/{.config/cava,cava.psf}
		fi

		software_id=118 # Mopidy
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP 'mopidy*'
			[[ -f '/etc/apt/sources.list.d/mopidy.list' ]] && rm /etc/apt/sources.list.d/mopidy.list

			command -v pip2 > /dev/null && pip2 uninstall -y Mopidy-MusicBox-Webclient Mopidy-Local-Images
			command -v pip3 > /dev/null && pip3 uninstall -y Mopidy-MusicBox-Webclient

			getent passwd mopidy > /dev/null && userdel mopidy
			[[ -d '/mnt/dietpi_userdata/mopidy' ]] && rm -R /mnt/dietpi_userdata/mopidy
			[[ -d '/etc/systemd/system/mopidy.service.d' ]] && rm -R /etc/systemd/system/mopidy.service.d

		fi

		software_id=31 # Kodi
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP kodi kodi-odroid kodi-aml-fbdev-odroid
			[[ -f '/usr/share/applications/kodi.desktop' ]] && rm /usr/share/applications/kodi.desktop
			rm -f /{root,home/*}/Desktop/kodi.desktop
			[[ -f '/etc/udev/rules.d/99-dietpi-kodi.rules' ]] && rm /etc/udev/rules.d/99-dietpi-kodi.rules
			[[ -f '/etc/modules-load.d/dietpi-n2-kodi.conf' ]] && rm /etc/modules-load.d/dietpi-n2-kodi.conf
			[[ -f '/etc/modules-load.d/dietpi-c4-kodi.conf' ]] && rm /etc/modules-load.d/dietpi-c4-kodi.conf

		fi

		software_id=39 # MiniDLNA
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/minidlna.service' ]]; then

				systemctl disable --now minidlna
				rm -R /etc/systemd/system/minidlna.service*

			fi
			[[ -d '/etc/systemd/system/minidlna.service.d' ]] && rm -R /etc/systemd/system/minidlna.service.d
			G_AGP minidlna
			getent passwd minidlna > /dev/null && userdel minidlna
			getent group minidlna > /dev/null && groupdel minidlna
			[[ -d '/mnt/dietpi_userdata/.MiniDLNA_Cache' ]] && rm -R /mnt/dietpi_userdata/.MiniDLNA_Cache

		fi

		software_id=51 # OpenTyrian
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			apt-mark auto libsdl1.2debian libsdl-net1.2

			rm -f /{usr/share/applications,{root,home/*}/Desktop}/opentyrian.desktop
			[[ -d '/usr/games/opentyrian' ]] && rm -R /usr/games/opentyrian

		fi

		software_id=59 # RPi Cam Control
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/raspimjpeg.service' ]]; then

				systemctl disable --now raspimjpeg
				rm -R /etc/systemd/system/raspimjpeg.service*

			fi
			[[ -d '/etc/systemd/system/raspimjpeg.service.d' ]] && rm -R /etc/systemd/system/raspimjpeg.service.d
			G_AGP gpac
			[[ -d '/var/www/rpicam' ]] && rm -R /var/www/rpicam
			[[ -f '/opt/vc/bin/raspimjpeg' ]] && rm /opt/vc/bin/raspimjpeg
			rm -f /usr/bin/raspimjpeg
			[[ -f '/var/lib/dietpi/dietpi-software/installed/raspimjpeg.sh' ]] && rm /var/lib/dietpi/dietpi-software/installed/raspimjpeg.sh
			[[ -f '/etc/raspimjpeg' ]] && rm /etc/raspimjpeg

		fi

		software_id=45 # Deluge
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/deluge-web.service' ]]; then

				systemctl disable --now deluge-web
				rm -R /etc/systemd/system/deluge-web.service*

			fi
			[[ -d '/etc/systemd/system/deluge-web.service.d' ]] && rm -R /etc/systemd/system/deluge-web.service.d
			if [[ -f '/etc/systemd/system/deluged.service' ]]; then

				systemctl disable --now deluged
				rm -R /etc/systemd/system/deluged.service*

			fi
			[[ -d '/etc/systemd/system/deluged.service.d' ]] && rm -R /etc/systemd/system/deluged.service.d
			G_AGP deluged deluge-web deluge-console
			getent passwd debian-deluged > /dev/null && userdel debian-deluged
			getent group debian-deluged > /dev/null && groupdel debian-deluged
			[[ -d '/mnt/dietpi_userdata/deluge' ]] && rm -R /mnt/dietpi_userdata/deluge

		fi

		software_id=94 # ProFTPD
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP proftpd-basic

		fi

		software_id=96 # Samba Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP samba samba-common-bin
			[[ -f '/etc/tmpfiles.d/dietpi-samba_cache.conf' ]] && rm /etc/tmpfiles.d/dietpi-samba_cache.conf
			[[ -d '/run/samba-cache' ]] && rm -R /run/samba-cache
			rm -Rf /var/cache/samba # Symlink

		fi

		software_id=95 # vsftpd
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP vsftpd

		fi

		software_id=109 # NFS Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP nfs-kernel-server
			rm -Rfv /etc/exports{,.d}

		fi

		software_id=67 # No-IP
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/noip2.service' ]]; then

				systemctl disable --now noip2
				rm -R /etc/systemd/system/noip2.service*

			fi
			[[ -d '/etc/systemd/system/noip2.service.d' ]] && rm -R /etc/systemd/system/noip2.service.d
			[[ -f '/usr/local/bin/noip2' ]] && rm /usr/local/bin/noip2
			[[ -f '/usr/local/etc/no-ip2.conf' ]] && rm /usr/local/etc/no-ip2.conf

		fi

		software_id=93 # Pi-hole
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			command -v pihole > /dev/null && pihole uninstall

			# pihole-FTL service+binary
			if [[ -f '/etc/init.d/pihole-FTL' ]]; then

				systemctl unmask pihole-FTL
				systemctl disable --now pihole-FTL
				rm /etc/init.d/pihole-FTL
				update-rc.d -f pihole-FTL remove

			fi
			[[ -d '/etc/systemd/system/pihole-FTL.service.d' ]] && rm -R /etc/systemd/system/pihole-FTL.service.d
			[[ -f '/usr/bin/pihole-FTL' ]] && rm /usr/bin/pihole-FTL

			# Dirs
			[[ -d '/etc/pihole' ]] && rm -R /etc/pihole
			[[ -d '/etc/.pihole' ]] && rm -R /etc/.pihole
			[[ -d '/opt/pihole' ]] && rm -R /opt/pihole
			[[ -d '/var/www/html/admin' ]] && rm -R /var/www/html/admin
			[[ -d '/var/www/html/pihole' ]] && rm -R /var/www/html/pihole

			# Symlinks
			[[ -L '/var/www/pihole' ]] && rm /var/www/pihole
			[[ -L '/var/www/admin' ]] && rm /var/www/admin

			# Pre Pi-hole v4
			G_AGP dnsmasq
			rm -Rf /etc/dnsmasq*

			# Disable and remove webserver configs
			# - Apache
			if [[ -d '/etc/apache2/sites-available' ]]; then

				a2dissite dietpi-pihole* 2> /dev/null
				rm -f /etc/apache2/sites-available/dietpi-pihole*

			fi
			# - Lighttpd
			if [[ -d '/etc/lighttpd/mods-available' ]]; then

				lighty-disable-mod dietpi-pihole* 2> /dev/null
				rm -f /etc/lighttpd/mods-available/99-dietpi-pihole*

			fi
			# - Nginx
			[[ -d '/etc/nginx/sites-dietpi' ]] && rm -f /etc/nginx/sites-dietpi/dietpi-pihole*

			# Unbound
			if (( ${aSOFTWARE_INSTALL_STATE[182]} > 1 )) && [[ -f '/etc/unbound/unbound.conf.d/dietpi-pihole.conf' ]]
			then
				G_DIETPI-NOTIFY 2 'Reconfiguring Unbound to work on its own'
				rm /etc/unbound/unbound.conf.d/dietpi-pihole.conf
			fi
		fi

		software_id=33 # Airsonic
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/airsonic.service' ]]; then

				systemctl disable --now airsonic
				rm -R /etc/systemd/system/airsonic.service*

			fi
			[[ -d '/etc/systemd/system/airsonic.service.d' ]] && rm -R /etc/systemd/system/airsonic.service.d
			getent passwd airsonic > /dev/null && userdel airsonic
			getent group airsonic > /dev/null && groupdel airsonic # Pre-v6.33
			[[ -d '/mnt/dietpi_userdata/airsonic' ]] && rm -R /mnt/dietpi_userdata/airsonic

		fi

		software_id=34 # Subsonic
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP subsonic
			[[ -d '/etc/systemd/system/subsonic.service.d' ]] && rm -R /etc/systemd/system/subsonic.service.d
			getent passwd subsonic > /dev/null && userdel subsonic
			getent group subsonic > /dev/null && groupdel subsonic
			[[ -d '/var/subsonic' ]] && rm -R /var/subsonic

		fi

		software_id=71
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			update-rc.d webiopi remove
			rm -R /etc/webiopi
			rm -R /usr/share/webiopi
			rm /usr/bin/webiopi
			rm /etc/init.d/webiopi

		fi

		software_id=68
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP connectd weavedconnectd # pre-v6.22

		fi

		software_id=98 # HAProxy
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/haproxy.service' ]]; then

				systemctl disable --now haproxy
				rm -R /etc/systemd/system/haproxy.service*

			fi
			[[ -d '/etc/systemd/system/haproxy.service.d' ]] && rm -R /etc/systemd/system/haproxy.service.d
			rm -Rf /{etc,var/lib,/usr/local/{doc,sbin}}/haproxy /usr/local/share/man/man1/haproxy.1 /etc/systemd/system/haproxy.service.d

		fi

		software_id=35 # Logitech Media Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP logitechmediaserver
			getent passwd squeezeboxserver > /dev/null && userdel squeezeboxserver
			getent group squeezeboxserver > /dev/null && groupdel squeezeboxserver
			[[ -f '/etc/systemd/system/logitechmediaserver.service' ]] && rm /etc/systemd/system/logitechmediaserver.service
			[[ -d '/var/lib/squeezeboxserver' ]] && rm -R /var/lib/squeezeboxserver
			[[ -d '/usr/share/squeezeboxserver' ]] && rm -R /usr/share/squeezeboxserver

		fi

		software_id=55 # Wordpress
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			systemctl start mariadb
			mysqladmin drop wordpress -f
			mysql -e 'drop user wordpress@localhost'
			rm -R /var/www/wordpress

		fi

		software_id=38 # FreshRSS
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling

			crontab -u www-data -l | grep -v '/opt/FreshRSS/app/actualize_script.php' | crontab -u www-data -

			a2disconf dietpi-freshrss 2> /dev/null
			[[ -f '/etc/apache2/conf-available/dietpi-freshrss.conf' ]] && rm -v /etc/apache2/conf-available/dietpi-freshrss.conf

			systemctl start mariadb
			mysqladmin drop freshrss -f
			mysql -e "drop user 'freshrss'@'localhost'"

			rm -R /var/www/freshrss
			rm -R /opt/FreshRSS
			rm /var/log/freshrss.log

		fi

		# TigerVNC/RealVNC
		if (( ${aSOFTWARE_INSTALL_STATE[28]} == -1 || ${aSOFTWARE_INSTALL_STATE[120]} == -1 )); then

			(( ${aSOFTWARE_INSTALL_STATE[28]} == -1 )) && software_id=28 || software_id=120
			Banner_Uninstalling

			# RealVNC services
			systemctl unmask vncserver-x11-serviced 2> /dev/null
			systemctl disable --now vncserver-x11-serviced 2> /dev/null
			systemctl unmask vncserver-virtuald 2> /dev/null
			systemctl disable --now vncserver-virtuald 2> /dev/null

			apt-mark auto dbus-user-session

			G_AGP 'tigervnc-*' x11vnc realvnc-vnc-server

			if [[ -f '/etc/systemd/system/vncserver.service' ]]; then

				systemctl disable --now vncserver
				rm -R /etc/systemd/system/vncserver.service*

			fi
			[[ -d '/etc/systemd/system/vncserver.service.d' ]] && rm -R /etc/systemd/system/vncserver.service.d
			if [[ -f '/etc/init.d/vncserver' ]]; then

				rm /etc/init.d/vncserver
				update-rc.d -f vncserver remove

			fi
			[[ -f '/usr/local/bin/vncserver' ]] && rm /usr/local/bin/vncserver
			rm -Rf /{root,home/*}/.vnc

		fi

		software_id=73 # Fail2Ban
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP fail2ban
			dpkg-query -s python3-systemd &> /dev/null && G_EXEC apt-mark auto python3-systemd
			[[ -d '/etc/fail2ban' ]] && G_EXEC_NOHALT=1 G_EXEC rm -R /etc/fail2ban

		fi

		software_id=64 # phpSysInfo
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			[[ -d '/var/www/phpsysinfo' ]] && G_EXEC_NOHALT=1 G_EXEC rm -R /var/www/phpsysinfo

		fi

		software_id=56 # Single File PHP Gallery
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			[[ -f '/var/www/gallery/index.php' ]] && rm /var/www/gallery/index.php
			[[ -f '/var/www/gallery/readme.txt' ]] && rm /var/www/gallery/readme.txt
			[[ -d '/var/www/gallery/_sfpg_data' ]] && rm -R /var/www/gallery/_sfpg_data

		fi

		software_id=40 # Ampache
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			[[ -d '/var/www/ampache' ]] && rm -R /var/www/ampache

			# Drop database
			systemctl start mariadb
			mysqladmin drop ampache -f
			mysql -e 'drop user ampache@localhost'

		fi

		software_id=117 # PiVPN
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			pivpn -u
			getent passwd pivpn > /dev/null && userdel pivpn
			getent group pivpn > /dev/null && groupdel pivpn
			[[ -f '/etc/apt/sources.list.d/swupdate.openvpn.net.list' ]] && rm /etc/apt/sources.list.d/swupdate.openvpn.net.list # Not part of current installer anymore

		fi

		software_id=97 # OpenVPN Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP openvpn
			[[ -d '/etc/openvpn' ]] && rm -R /etc/openvpn
			[[ -f '/etc/sysctl.d/dietpi-openvpn.conf' ]] && rm /etc/sysctl.d/dietpi-openvpn.conf

		fi

		software_id=172 # WireGuard
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP wireguard*
			[[ -d '/etc/wireguard' ]] && rm -R /etc/wireguard
			[[ -d '/etc/systemd/system/wg-quick@wg0.service.d' ]] && rm -R /etc/systemd/system/wg-quick@wg0.service.d
			[[ -f '/etc/apt/sources.list.d/dietpi-wireguard.list' ]] && rm /etc/apt/sources.list.d/dietpi-wireguard.list
			[[ -f '/etc/apt/preferences.d/dietpi-wireguard' ]] && rm /etc/apt/preferences.d/dietpi-wireguard

		fi

		software_id=171 # DietPi-NordVPN
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/dietpi-nordvpn.service' ]]; then

				systemctl disable --now dietpi-nordvpn
				rm -R /etc/systemd/system/dietpi-nordvpn.service*

			fi
			[[ -d '/etc/systemd/system/dietpi-nordvpn.service.d' ]] && rm -R /etc/systemd/system/dietpi-nordvpn.service.d
			rm -Rf /etc/openvpn/ovpn_{tcp,udp}/*nordvpn* /var/lib/dietpi/dietpi-software/installed/dietpi-nordvpn
			rmdir --ignore-fail-on-non-empty /etc/openvpn/ovpn_{tcp,udp}

		fi

		software_id=92 # Certbot
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP python3-certbot-apache python3-certbot-nginx certbot
			[[ -d '/etc/systemd/system/certbot.service.d' ]] && rm -R /etc/systemd/system/certbot.service.d
			# Pre-v6.3
			if [[ -f '/etc/systemd/system/certbot.service' ]]; then

				systemctl disable --now certbot
				rm /etc/systemd/system/certbot.service

			fi

		fi

		software_id=69
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP python-rpi.gpio python3-rpi.gpio

		fi

		software_id=72 # I2C
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP i2c-tools
			/boot/dietpi/func/dietpi-set_hardware i2c disable

		fi

		software_id=70
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			rm -Rf /root/wiringPi*

		fi

		software_id=61 # Tor Hotspot
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP tor

			[[ -d '/var/log/tor' ]] && rm -R /var/log/tor # pre-v6.27

			# Uninstall WIFIHOTSPOT ALSO. Due to IPtables needing reset
			aSOFTWARE_INSTALL_STATE[60]=-1

		fi

		software_id=60 # WiFi Hotspot
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP hostapd isc-dhcp-server

			[[ -f '/etc/dhcp/dhcpd.conf' ]] && rm /etc/dhcp/dhcpd.conf
			[[ -f '/etc/hostapd/hostapd.conf' ]] && rm /etc/hostapd/hostapd.conf
			[[ -f '/etc/default/isc-dhcp-server' ]] && rm /etc/default/isc-dhcp-server
			[[ -f '/etc/etc/default/hostapd' ]] && rm /etc/default/hostapd
			[[ -f '/etc/iptables.ipv4.nat' ]] && rm /etc/iptables.ipv4.nat
			# Remove binary (used a -f trigger to detect wifi hotspot mode in dietpi-config).
			[[ -f '/usr/sbin/hostapd' ]] && rm /usr/sbin/hostapd
			[[ -f '/usr/sbin/hostapd_cli' ]] && rm /usr/sbin/hostapd_cli

			# IP forwarding
			[[ -f '/etc/sysctl.d/dietpi-wifihotspot.conf' ]] && rm /etc/sysctl.d/dietpi-wifihotspot.conf

			# Set WiFi interface back to inactive and ready for use with dietpi-config.
			local wifi_index=$(mawk 'NR==2' /run/dietpi/.network)
			# - Remove all entries below wlan, so we can recreate them
			sed -i '/allow-hotplug wlan/q0' /etc/network/interfaces
			# - Disable
			sed -i "/allow-hotplug wlan/c\#allow-hotplug wlan$wifi_index" /etc/network/interfaces
			# - Add default WiFi settings to network interfaces config
			cat << _EOF_ >> /etc/network/interfaces
iface wlan$wifi_index inet dhcp
address 192.168.0.101
netmask 255.255.255.0
gateway 192.168.0.1
wireless-power off
wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf
#dns-nameservers 8.8.8.8 8.8.4.4
_EOF_
			# - Flush IP tables
			iptables -F
			iptables -t nat -F

		fi

		software_id=48
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			rm -R /var/www/pydio

			# Remove webserver configs
			command -v a2dissite > /dev/null && a2dissite dietpi-pydio
			[[ -f '/etc/apache2/conf-available/dietpi-pydio.conf' ]] && rm /etc/apache2/conf-available/dietpi-pydio.conf
			[[ -f '/etc/nginx/sites-dietpi/dietpi-pydio.conf' ]] && rm /etc/nginx/sites-dietpi/dietpi-pydio.conf
			command -v lighty-disable-mod > /dev/null && lighty-disable-mod dietpi-pydio
			[[ -f '/etc/lighttpd/conf-available/99-dietpi-pydio.conf' ]] && rm /etc/lighttpd/conf-available/99-dietpi-pydio.conf

			# Drop database
			systemctl start mariadb
			mysqladmin drop pydio -f
			mysql -e "drop user pydio@localhost"

		fi

		software_id=36 # Squeezelite
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP squeezelite
			rm -Rfv /etc/systemd/system/squeezelite.service*
			getent passwd squeezelite > /dev/null && userdel squeezelite
			getent group squeezelite > /dev/null && groupdel squeezelite

		fi

		software_id=99 # EmonPi
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/init.d/emonhub' ]]; then

				systemctl unmask emonhub
				systemctl disable --now emonhub
				rm /etc/init.d/emonhub
				update-rc.d -f emonhub remove

			fi
			[[ -d '/etc/systemd/system/emonhub.service.d' ]] && rm -R /etc/systemd/system/emonhub.service.d
			[[ -f '/etc/default/emonhub' ]] && rm /etc/default/emonhub
			[[ -d '/etc/emonhub' ]] && rm -R /etc/emonhub

		fi

		software_id=66
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP rpimonitor

		fi

		software_id=57 # Baïkal
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling

			# Remove webserver configs
			# - Ligttpd
			if [[ -f '/etc/lighttpd/conf-available/99-dietpi-dav_redirect.conf' ]]; then

				command -v lighty-disable-mod > /dev/null && lighty-disable-mod dietpi-dav_redirect
				rm -v /etc/lighttpd/conf-available/99-dietpi-dav_redirect.conf
			fi
			if [[ -f '/etc/lighttpd/conf-available/99-dietpi-baikal.conf' ]]; then

				command -v lighty-disable-mod > /dev/null && lighty-disable-mod dietpi-baikal
				rm -v /etc/lighttpd/conf-available/99-dietpi-baikal.conf
			fi
			# - Apache
			if [[ -f '/etc/apache2/conf-available/dietpi-dav_redirect.conf' ]]; then

				command -v a2disconf > /dev/null && a2disconf dietpi-dav_redirect
				rm -v /etc/apache2/conf-available/dietpi-dav_redirect.conf

			fi
			if [[ -f '/etc/apache2/sites-available/dietpi-baikal.conf' ]]; then

				command -v a2dissite > /dev/null && a2dissite dietpi-baikal
				rm -v /etc/apache2/sites-available/dietpi-baikal.conf

			fi
			# - Nginx
			[[ -f '/etc/nginx/sites-dietpi/dietpi-dav_redirect.conf' ]] && rm -v /etc/nginx/sites-dietpi/dietpi-dav_redirect.conf
			[[ -f '/etc/nginx/sites-dietpi/dietpi-baikal.conf' ]] && rm -v /etc/nginx/sites-dietpi/dietpi-baikal.conf

			[[ -d '/var/www/baikal' ]] && rm -R /var/www/baikal

			# Drop database
			systemctl start mariadb
			mysqladmin drop baikal -f
			mysql -e 'drop user baikal@localhost'

		fi

		software_id=65 # Netdata
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling

			# 1.2.0+
			G_AGP netdata

			# Pre-v6.22 Raspbian Stretch + Pre-v6.26 Raspbian Stretch ARMv6 only
			[[ -f '/etc/systemd/system/netdata.service' ]] && rm /etc/systemd/system/netdata.service
			getent passwd netdata > /dev/null && userdel netdata
			getent group netdata > /dev/null && groupdel netdata

			# 1.0.0
			[[ -f '/usr/sbin/netdata' ]] && rm /usr/sbin/netdata
			[[ -d '/etc/netdata' ]] && rm -R /etc/netdata
			[[ -d '/usr/lib/netdata' ]] && rm -R /usr/lib/netdata
			[[ -d '/usr/share/netdata' ]] && rm -R /usr/share/netdata
			[[ -d '/usr/libexec/netdata' ]] && rm -R /usr/libexec/netdata
			[[ -d '/var/cache/netdata' ]] && rm -R /var/cache/netdata
			[[ -d '/var/log/netdata' ]] && rm -R /var/log/netdata
			[[ -d '/var/lib/netdata' ]] && rm -R /var/lib/netdata

		fi

		software_id=43
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP mumble-server

		fi

		software_id=41 # Emby Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP emby-server embymagick
			[[ -f '/etc/apt/sources.list.d/emby-server.list' ]] && rm /etc/apt/sources.list.d/emby-server.list # Pre v6.15

		fi

		software_id=58 # OpenBazaar
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 ))
		then
			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/openbazaar.service' ]]
			then
				systemctl disable --now openbazaar
				rm -R /etc/systemd/system/openbazaar.service*
			fi
			[[ -d '/etc/systemd/system/openbazaar.service.d' ]] && rm -R /etc/systemd/system/openbazaar.service.d
			getent passwd openbazaar > /dev/null && userdel openbazaar
			getent group openbazaar > /dev/null && groupdel openbazaar
			if [[ -f '/etc/bashrc.d/go.sh' ]]
			then
				. /etc/bashrc.d/go.sh
				go clean -i github.com/OpenBazaar...
			fi
			[[ -d '/mnt/dietpi_userdata/go/src/github.com/OpenBazaar' ]] && rm -R /mnt/dietpi_userdata/go/src/github.com/OpenBazaar
			[[ -d '/mnt/dietpi_userdata/go/src/github.com' ]] && rmdir --ignore-fail-on-non-empty /mnt/dietpi_userdata/go/src/github.com
			[[ -d '/etc/openbazaar-server' ]] && rm -R /etc/openbazaar-server # Pre v6.15 OB1.0
		fi

		software_id=42 # Plex Media Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP plexmediaserver
			# Remove systemd unit which currently survives the package purging: https://github.com/MichaIng/DietPi/issues/3551
			if [[ -f '/lib/systemd/system/plexmediaserver.service' ]]; then

				systemctl unmask plexmediaserver
				systemctl disable --now plexmediaserver
				rm -R /lib/systemd/system/plexmediaserver.service*

			fi
			[[ -d '/var/lib/plexmediaserver' ]] && rm -R /var/lib/plexmediaserver # Left over from purging package, still...
			[[ -d '/etc/systemd/system/plexmediaserver.service.d' ]] && rm -R /etc/systemd/system/plexmediaserver.service.d

		fi

		software_id=52 # Cuberite
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/cuberite.service' ]]; then

				systemctl disable --now cuberite
				rm -R /etc/systemd/system/cuberite.service*

			fi
			[[ -d '/etc/systemd/system/cuberite.service.d' ]] && rm -R /etc/systemd/system/cuberite.service.d
			getent passwd cuberite > /dev/null && userdel cuberite
			getent group cuberite > /dev/null && groupdel cuberite
			[[ -d '/mnt/dietpi_userdata/cuberite' ]] && rm -R /mnt/dietpi_userdata/cuberite

		fi

		software_id=53 # MineOS
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 ))
		then
			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/mineos.service' ]]
			then
				systemctl disable --now mineos
				rm -R /etc/systemd/system/mineos.service*
			fi
			[[ -d '/etc/systemd/system/mineos.service.d' ]] && rm -R /etc/systemd/system/mineos.service.d
			[[ -d '/mnt/dietpi_userdata/mineos' ]] && rm -R /mnt/dietpi_userdata/mineos
			[[ -f '/etc/mineos.conf' ]] && rm /etc/mineos.conf
			rm -Rf /var/games/minecraft /usr/local/bin/mineos # Symlinks
			getent passwd mineos > /dev/null && userdel mineos # pre-v6.34
			getent group mineos > /dev/null && groupdel mineos # pre-v6.34
			[[ -d '/home/mineos' ]] && rm -R /home/mineos # pre-v6.34
		fi

		software_id=49 # Gogs
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling

			if [[ -f '/etc/systemd/system/gogs.service' ]]; then

				systemctl disable --now gogs
				rm -R /etc/systemd/system/gogs.service*

			fi
			[[ -d '/etc/systemd/system/gogs.service.d' ]] && rm -R /etc/systemd/system/gogs.service.d

			getent passwd gogs > /dev/null && userdel gogs
			getent group gogs > /dev/null && groupdel gogs

			[[ -d '/etc/gogs' ]] && rm -R /etc/gogs
			[[ -d '/var/log/gogs' ]] && rm -R /var/log/gogs

			systemctl start mariadb
			mysqladmin drop gogs -f
			mysql -e 'drop user gogs@localhost'

		fi

		software_id=46 # qBittorrent
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/qbittorrent.service' ]]; then

				systemctl disable --now qbittorrent
				G_EXEC_NOHALT=1 G_EXEC rm -R /etc/systemd/system/qbittorrent.service*

			fi
			[[ -d '/etc/systemd/system/qbittorrent.service.d' ]] && G_EXEC_NOHALT=1 G_EXEC rm -R /etc/systemd/system/qbittorrent.service.d
			G_AGP qbittorrent-nox
			getent passwd qbittorrent > /dev/null && userdel qbittorrent
			getent group qbittorrent > /dev/null && groupdel qbittorrent # pre-v6.29
			[[ -d '/home/qbittorrent' ]] && G_EXEC_NOHALT=1 G_EXEC rm -R /home/qbittorrent
			[[ -d '/var/log/qbittorrent' ]] && G_EXEC_NOHALT=1 G_EXEC rm -R /var/log/qbittorrent

		fi

		software_id=50 # Syncthing
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling

			if [[ -f '/etc/systemd/system/syncthing.service' ]]; then

				systemctl disable --now syncthing
				rm -R /etc/systemd/system/syncthing.service*

			fi
			[[ -d '/opt/syncthing' ]] && rm -R /opt/syncthing
			[[ -d '/mnt/dietpi_userdata/syncthing' ]] && rm -R /mnt/dietpi_userdata/syncthing
			[[ -f '/etc/sysctl.d/dietpi-syncthing.conf' ]] && rm /etc/sysctl.d/dietpi-syncthing.conf

		fi

		software_id=116 # Medusa
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 ))
		then
			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/medusa.service' ]]
			then
				systemctl disable --now medusa
				rm -R /etc/systemd/system/medusa.service*
			fi
			[[ -d '/etc/systemd/system/medusa.service.d' ]] && rm -R /etc/systemd/system/medusa.service.d
			[[ -d '/mnt/dietpi_userdata/medusa' ]] && rm -R /mnt/dietpi_userdata/medusa
			getent passwd medusa > /dev/null && userdel medusa
			getent group medusa > /dev/null && groupdel medusa # Pre-v6.33
		fi

		software_id=107 # rTorrent
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			# ruTorrent
			[[ -d '/var/www/rutorrent' ]] && rm -R /var/www/rutorrent
			# - Authentication
			[[ -f '/etc/.rutorrent-htaccess' ]] && rm /etc/.rutorrent-htaccess
			# - Lighttpd
			#Remove from #RUTORRENT_DIETPI to #RUTORRENT_DIETPI in /etc/lighttpd/lighttpd.conf
			# - Nginx
			[[ -f '/etc/nginx/sites-dietpi/dietpi-rutorrent.conf' ]] && rm /etc/nginx/sites-dietpi/dietpi-rutorrent.conf
			# - Apache2
			command -v a2dissite &> /dev/null && a2dissite dietpi-rutorrent
			[[ -f '/etc/apache2/sites-available/dietpi-rutorrent.conf' ]] && rm /etc/apache2/sites-available/dietpi-rutorrent.conf

			# rTorrent
			if [[ -f '/etc/systemd/system/rtorrent.service' ]]; then

				systemctl disable --now rtorrent
				rm -R /etc/systemd/system/rtorrent.service*

			fi
			[[ -d '/etc/systemd/system/rtorrent.service.d' ]] && rm -R /etc/systemd/system/rtorrent.service.d
			G_AGP rtorrent
			getent passwd rtorrent && userdel rtorrent
			getent group rtorrent && groupdel rtorrent # pre-v6.29
			[[ -d '/mnt/dietpi_userdata/rtorrent' ]] && rm -R /mnt/dietpi_userdata/rtorrent
			[[ -d '/mnt/dietpi_userdata/downloads/.session' ]] && rm -R /mnt/dietpi_userdata/downloads/.session

		fi

		software_id=108 # Amiberry
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			# Service
			if [[ -f '/etc/systemd/system/amiberry.service' ]]; then

				systemctl disable --now amiberry
				rm -R /etc/systemd/system/amiberry.service*

			fi
			[[ -d '/etc/systemd/system/amiberry.service.d' ]] && rm -R /etc/systemd/system/amiberry.service.d
			# Files
			[[ -d '/mnt/dietpi_userdata/amiberry' ]] && rm -R /mnt/dietpi_userdata/amiberry
			# Autostart index: If currently Amiberry, revert to console login
			[[ -f '/boot/dietpi/.dietpi-autostart_index' && $(</boot/dietpi/.dietpi-autostart_index) == [68] ]] && /boot/dietpi/dietpi-autostart 0

		fi

		# DXX-Rebirth
		software_id=112
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			rm -f /mnt/dietpi_userdata/dxx-rebirth/*
			rm -R /mnt/dietpi_userdata/dxx-rebirth/descent_1_game
			rm -R /mnt/dietpi_userdata/dxx-rebirth/descent_2_game

			# Remove symlinks
			rm -f /root/.d1x-rebirth
			rm -f /root/.d2x-rebirth
			rm -f /{root,home/*}/Desktop/dxx-rebirth.desktop
			rm -f /usr/share/applications/dxx-rebirth.desktop

		fi

		software_id=113 # Chromium
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling

			rm /etc/chromium.d/custom_flags
			rm /var/lib/dietpi/dietpi-software/installed/chromium-autostart.sh
			rm /root/.chromium-browser.init

			G_AGP chromium*

		fi

		software_id=157 # Home Assistant
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling

			# Service
			if [[ -f '/etc/systemd/system/home-assistant.service' ]]; then

				systemctl disable --now home-assistant
				rm -R /etc/systemd/system/home-assistant.service*

			fi
			[[ -d '/etc/systemd/system/home-assistant.service.d' ]] && rm -R /etc/systemd/system/home-assistant.service.d

			# User and pyenv
			getent passwd homeassistant > /dev/null && userdel homeassistant
			getent group homeassistant > /dev/null && groupdel homeassistant
			[[ -d '/home/homeassistant' ]] && rm -R /home/homeassistant
			[[ -d '/srv/homeassistant' ]] && rm -R /srv/homeassistant # pre-v6-27

			G_DIETPI-NOTIFY 2 'Home Assistant data and settings are not removed: /mnt/dietpi_userdata/homeassistant
 - You might want to do this manually: rm -R /mnt/dietpi_userdata/homeassistant'

		fi

		software_id=140 # Domoticz
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			if [[ -f '/etc/systemd/system/domoticz.service' ]]; then

				systemctl disable --now domoticz
				rm -R /etc/systemd/system/domoticz.service*

			fi
			[[ -d '/etc/systemd/system/domoticz.service.d' ]] && rm -R /etc/systemd/system/domoticz.service.d
			getent passwd domoticz > /dev/null && userdel domoticz
			getent group domoticz > /dev/null && groupdel domoticz
			[[ -d '/opt/domoticz' ]] && rm -R /opt/domoticz
			[[ -d '/mnt/dietpi_userdata/domoticz' ]] && rm -R /mnt/dietpi_userdata/domoticz

		fi

		software_id=165 # Gitea
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			# Remove service
			if [[ -f '/etc/systemd/system/gitea.service' ]]; then

				systemctl disable --now gitea
				rm -R /etc/systemd/system/gitea.service*

			fi
			[[ -d '/etc/systemd/system/gitea.service.d' ]] && rm -R /etc/systemd/system/gitea.service.d

			# Delete data
			[[ -d '/mnt/dietpi_userdata/gitea' ]] && rm -R /mnt/dietpi_userdata/gitea
			[[ -d '/var/log/gitea' ]] && rm -R /var/log/gitea

			# Drop/delete database
			systemctl start mariadb
			mysqladmin drop gitea -f
			mysql -e 'drop user gitea@localhost'

		fi

		software_id=166
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/pi-spc.service' ]]; then

				systemctl disable --now pi-spc
				rm -R /etc/systemd/system/pi-spc.service*

			fi
			[[ -d '/etc/systemd/system/pi-spc.service.d' ]] && rm -R /etc/systemd/system/pi-spc.service.d
			rm -R /var/lib/dietpi/dietpi-software/installed/pi-spc

			sed -i '/^[[:blank:]]*dtoverlay=gpio-shutdown/d' /boot/config.txt
			sed -i '/^[[:blank:]]*dtoverlay=gpio-poweroff/d' /boot/config.txt

		fi

		software_id=167
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP raspotify
			[[ -f '/etc/apt/sources.list.d/raspotify.list' ]] && rm /etc/apt/sources.list.d/raspotify.list

		fi

		software_id=169 # Google AIY
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			rm -R /mnt/dietpi_userdata/voice-recognizer-raspi
			rm /etc/systemd/system/voice-recognizer.service
			rm /etc/systemd/system/alsa-init.service
			rm -R /home/dietpi/assistant.json

		fi

		software_id=176 # Mycroft AI
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling

			# Service
			if [[ -f '/etc/systemd/system/mycroft.service' ]]; then

				systemctl disable --now mycroft
				rm -R /etc/systemd/system/mycroft.service*

			fi
			[[ -d '/etc/systemd/system/mycroft.service.d' ]] && rm -R /etc/systemd/system/mycroft.service.d

			# User
			getent passwd mycroft > /dev/null && userdel mycroft
			getent group mycroft > /dev/null && groupdel mycroft

			# Files
			[[ -d '/mnt/dietpi_userdata/mycroft-core' ]] && rm -R /mnt/dietpi_userdata/mycroft-core
			[[ -d '/mnt/dietpi_userdata/mycroft-data' ]] && rm -R /mnt/dietpi_userdata/mycroft-data
			[[ -d '/etc/mycroft' ]] && rm -R /etc/mycroft
			[[ -d '/var/log/mycroft' ]] && rm -R /var/log/mycroft
			rm -Rf /opt/mycroft # symlink
			[[ -f '/etc/bashrc.d/mycroft.sh' ]] && rm /etc/bashrc.d/mycroft.sh
			[[ -f '/etc/sudoers.d/mycroft' ]] && rm /etc/sudoers.d/mycroft

		fi

		software_id=100 # PiJuice
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP pijuice-base
			[[ -d '/var/lib/dietpi/dietpi-software/installed/pijuice' ]] && G_EXEC_NOHALT=1 G_EXEC rm -R /var/lib/dietpi/dietpi-software/installed/pijuice

		fi

		software_id=158 # MinIO
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling

			# Service
			if [[ -f '/etc/systemd/system/minio.service' ]]; then

				systemctl disable --now minio
				rm -R /etc/systemd/system/minio.service*

			fi
			[[ -d '/etc/systemd/system/minio.service.d' ]] && rm -R /etc/systemd/system/minio.service.d

			# Files
			[[ -f '/usr/local/bin/minio' ]] && rm -v /usr/local/bin/minio
			[[ -f '/etc/default/minio' ]] && rm -v /etc/default/minio

			# User
			getent passwd minio-user > /dev/null && userdel minio-user
			getent group minio-user > /dev/null && groupdel minio-user

			# Certbot hook
			[[ -f '/etc/systemd/system/certbot.service.d/dietpi-minio.conf' ]] && rm -v /etc/systemd/system/certbot.service.d/dietpi-minio.conf
			[[ -d '/etc/systemd/system/certbot.service.d' ]] && rmdir --ignore-fail-on-non-empty -v /etc/systemd/system/certbot.service.d

		fi

		software_id=161 # FuguHub
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling

			# Service
			if [[ -f '/etc/init.d/bdd' ]]; then

				systemctl unmask bdd
				systemctl disable --now bdd
				rm -v /etc/init.d/bdd
				update-rc.d -f bdd remove

			fi
			[[ -d '/etc/systemd/system/bdd.service.d' ]] && rm -R /etc/systemd/system/bdd.service.d

			# User
			getent passwd bd > /dev/null && userdel bd
			getent group bd > /dev/null && groupdel bd

			# Files
			[[ -d '/home/bd' ]] && rm -R /home/bd

		fi

		software_id=162 # Docker
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling

			# Remove docker, all its unused dependencies and custom repository
			G_AGP docker-ce docker-ce-cli docker-engine
			[[ -f '/etc/apt/sources.list.d/docker.list' ]] && G_EXEC_NOEXIT=1 G_EXEC rm /etc/apt/sources.list.d/docker.list
			[[ -f '/etc/apt/trusted.gpg.d/dietpi-docker.gpg' ]] && G_EXEC_NOEXIT=1 G_EXEC rm /etc/apt/trusted.gpg.d/dietpi-docker.gpg

			# Remove service adjustments
			[[ -d '/lib/systemd/system/docker.service.d' ]] && G_EXEC_NOEXIT=1 G_EXEC rm -R /lib/systemd/system/docker.service.d
			# Delete data files - dietpi
			[[ -d '/mnt/dietpi_userdata/docker-data' ]] && G_EXEC_NOEXIT=1 G_EXEC rm -R /mnt/dietpi_userdata/docker-data
			# Remove default unused folder
			[[ -d '/var/lib/docker' ]] && G_EXEC_NOEXIT=1 G_EXEC rm -R /var/lib/docker
			# Remove configuration file
			[[ -d '/etc/docker' ]] && G_EXEC_NOEXIT=1 G_EXEC rm -R /etc/docker

			# Set Portainer as not installed
			aSOFTWARE_INSTALL_STATE[185]=0

		fi

		software_id=185 # Portainer
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling

			# Check if docker is still installed
			if [[ -d '/mnt/dietpi_userdata/docker-data' ]]
			then
				# Restart Docker service to be able to remove the container
				G_EXEC systemctl restart docker

				# Remove portainer container, image & volume
				G_EXEC docker rm "$(docker ps -a | mawk '/portainer\/portainer(-ce)?( |$)/{print $1}')" --force
				G_EXEC docker rmi "$(docker images -a | mawk '/portainer\/portainer(-ce)?( |$)/{print $3}')"
				G_EXEC docker volume rm portainer_data
			fi

		fi

		software_id=134 # Docker Compose
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_EXEC_NOEXIT=1 G_EXEC_OUTPUT=1 G_EXEC pip3 uninstall -y docker-compose

		fi

		software_id=164 # Nukkit
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/nukkit.service' ]]; then

				systemctl disable --now nukkit
				rm -R /etc/systemd/system/nukkit.service*

			fi
			[[ -d '/etc/systemd/system/nukkit.service.d' ]] && rm -R /etc/systemd/system/nukkit.service.d
			[[ -e '/usr/local/bin/nukkit' ]] && rm -R /usr/local/bin/nukkit

		fi

		software_id=163 # GMediaRender
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			if [[ -f '/etc/systemd/system/gmrender.service' ]]; then

				systemctl disable --now gmrender
				rm -R /etc/systemd/system/gmrender.service*

			fi
			[[ -d '/etc/systemd/system/gmrender.service.d' ]] && rm -R /etc/systemd/system/gmrender.service.d
			getent passwd gmrender > /dev/null && userdel gmrender
			getent group gmrender > /dev/null && groupdel gmrender
			[[ -f '/usr/local/bin/gmediarender' ]] && rm /usr/local/bin/gmediarender

		fi

		software_id=159 # Allo web UI
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 || ${aSOFTWARE_INSTALL_STATE[160]} == -1 )); then

			Banner_Uninstalling
			[[ -d '/var/www/allo' ]] && rm -R /var/www/allo
			getent passwd allo > /dev/null && userdel allo
			[[ -f '/etc/sudoers.d/allo' ]] && rm /etc/sudoers.d/allo
			[[ -d '/home/allo' ]] && rm -R /home/allo
			systemctl start mariadb
			mysqladmin drop allo_db -f
			mysql -e 'drop user allo_db@localhost'

		fi

		software_id=88 # MariaDB
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			# Do a full database backup, if mariadb binary is still available: https://github.com/MichaIng/DietPi/issues/3257#issuecomment-568764107
			if command -v mysql > /dev/null; then

				G_WHIP_MSG "Creating MariaDB database backup before uninstallation:\n\nIn case of accident, we create a database backup for you. You can remove it manually, if you are sure, that you don't need it any more.\n\n/mnt/dietpi_userdata/mariadb-database-backup.sql"
				G_EXEC systemctl start mariadb
				mysqldump --all-databases > /mnt/dietpi_userdata/mariadb-database-backup.sql
				systemctl stop mariadb

			fi

			G_AGP mariadb-server php*-mysql
			rm -Rf /{mnt/dietpi_userdata,var/lib,var/log,etc}/mysql
			[[ -d '/root/.mysql_history' ]] && rm -R /root/.mysql_history

		fi

		software_id=74 # InfluxDB
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling

			if [[ -f '/lib/systemd/system/influxdb.service' ]]; then

				systemctl unmask influxdb
				systemctl disable --now influxdb
				rm -R /lib/systemd/system/influxdb.service*

			fi
			[[ -d '/etc/systemd/system/influxdb.service.d' ]] && rm -R /etc/systemd/system/influxdb.service.d
			G_AGP influxdb
			[[ -f '/etc/apt/sources.list.d/influxdb.list' ]] && rm /etc/apt/sources.list.d/influxdb.list
			[[ -f '/etc/apt/trusted.gpg.d/dietpi-influxdb.gpg' ]] && rm /etc/apt/trusted.gpg.d/dietpi-influxdb.gpg
			rm -Rf /var/lib/influxdb # Symlink
			[[ -d '/mnt/dietpi_userdata/influxdb' ]] && rm -R /mnt/dietpi_userdata/influxdb

		fi

		software_id=77 # Grafana
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP grafana
			[[ -f '/etc/apt/sources.list.d/grafana.list' ]] && rm /etc/apt/sources.list.d/grafana.list
			[[ -f '/etc/apt/trusted.gpg.d/dietpi-grafana.gpg' ]] && rm /etc/apt/trusted.gpg.d/dietpi-grafana.gpg
			getent passwd grafana > /dev/null && userdel grafana
			getent group grafana > /dev/null && groupdel grafana
			rm -Rfv /{mnt/dietpi_userdata,var/lib,var/log,etc}/grafana /etc/systemd/system/grafana.service.d

		fi

		software_id=80 # Ubooquity
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling

			if [[ -f '/etc/systemd/system/ubooquity.service' ]]; then

				systemctl disable --now ubooquity
				rm -R /etc/systemd/system/ubooquity.service*

			fi
			[[ -d '/etc/systemd/system/ubooquity.service.d' ]] && G_EXEC_NOHALT=1 G_EXEC rm -R /etc/systemd/system/ubooquity.service.d
			getent passwd ubooquity > /dev/null && userdel ubooquity
			getent group ubooquity > /dev/null && groupdel ubooquity
			[[ -d '/mnt/dietpi_userdata/ubooquity' ]] && G_EXEC_NOHALT=1 G_EXEC rm -R /mnt/dietpi_userdata/ubooquity

		fi

		software_id=179 # Komga
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling

			if [[ -f '/etc/systemd/system/komga.service' ]]; then

				systemctl disable --now komga
				rm -R /etc/systemd/system/komga.service*

			fi
			[[ -d '/etc/systemd/system/komga.service.d' ]] && G_EXEC_NOHALT=1 G_EXEC rm -R /etc/systemd/system/komga.service.d
			getent passwd komga > /dev/null && userdel komga
			getent group komga > /dev/null && groupdel komga
			[[ -d '/mnt/dietpi_userdata/komga' ]] && G_EXEC_NOHALT=1 G_EXEC rm -R /mnt/dietpi_userdata/komga

		fi

		software_id=86 # Roon Extension Manager
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			UNINSTALL_URL_ADDRESS='https://raw.githubusercontent.com/TheAppgineer/roon-extension-manager-packaging/master/linux/setup.sh'
			G_CHECK_URL "$UNINSTALL_URL_ADDRESS"
			G_EXEC curl -sSfL "$UNINSTALL_URL_ADDRESS" -o setup.sh
			G_EXEC chmod +x setup.sh
			G_EXEC_OUTPUT=1 G_EXEC ./setup.sh --uninstall
			G_EXEC_NOHALT=1 G_EXEC rm setup.sh

		fi

		software_id=178 # Jellyfin
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP jellyfin jellyfin-ffmpeg
			rm -f /usr/bin/jellyfin # Symlink created but left on package purge: https://github.com/jellyfin/jellyfin/pull/3690
			[[ -f '/etc/apt/sources.list.d/dietpi-jellyfin.list' ]] && G_EXEC_NOHALT=1 G_EXEC rm /etc/apt/sources.list.d/dietpi-jellyfin.list
			[[ -f '/etc/apt/trusted.gpg.d/dietpi-jellyfin.gpg' ]] && G_EXEC_NOHALT=1 G_EXEC rm /etc/apt/trusted.gpg.d/dietpi-jellyfin.gpg
			[[ -d '/etc/systemd/system/jellyfin.service.d' ]] && G_EXEC_NOHALT=1 G_EXEC rm -R /etc/systemd/system/jellyfin.service.d
			[[ -d '/mnt/dietpi_userdata/jellyfin' ]] && G_EXEC_NOHALT=1 G_EXEC rm -R /mnt/dietpi_userdata/jellyfin

		fi

		#----------------------------------------------------------------------
		# LINUX SOFTWARE

		software_id=15
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP tcpdump

		fi

		software_id=14
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP nload

		fi

		software_id=13
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP mtr-tiny

		fi

		software_id=11
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP iptraf

		fi

		software_id=10
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP iftop

		fi

		software_id=19
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP jed

		fi

		software_id=3
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP mc

		fi

		software_id=18
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP emacs

		fi

		software_id=20
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )) ||
			(( ${aSOFTWARE_INSTALL_STATE[21]} == -1 )); then

			Banner_Uninstalling
			G_AGP vim vim-tiny

		fi

		software_id=127 # Neovim
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP neovim

		fi

		software_id=0 # OpenSSH Client
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			# This also removes OpenSSH server. So lets check OpenSSH server isn't installed before hand.
			dpkg-query -s 'openssh-server' &> /dev/null || G_AGP openssh-client

		fi

		software_id=1
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			umount -f /mnt/samba
			G_AGP smbclient
			# Disable in fstab
			sed -i '/\/mnt\/samba/c\#\/mnt\/samba . Please use dietpi-drive_manager to setup this mount' /etc/fstab
			# Add info file for installation method.
			echo -e 'Samba client can be installed and setup by DietPi-Drive_Manager.\nSimply run: dietpi-drive_manager and select the "Add network drive" option.' > /mnt/samba/readme.txt

		fi

		software_id=111 # UrBackup Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP urbackup-server

			# Pre-v6.29: ARMv8 source build
			if [[ -f '/etc/systemd/system/urbackupsrv.service' ]]; then

				systemctl disable --now urbackupsrv
				rm -R /etc/systemd/system/urbackupsrv.service*

			fi
			[[ -d '/etc/systemd/system/urbackupsrv.service.d' ]] && rm -R /etc/systemd/system/urbackupsrv.service.d
			[[ -d '/mnt/dietpi_userdata/urbackup' ]] && rm -R /mnt/dietpi_userdata/urbackup
			[[ -d '/usr/share/urbackup' ]] && rm -R /usr/share/urbackup
			[[ -d '/usr/local/share/urbackup' ]] && rm -R /usr/local/share/urbackup
			[[ -d '/var/urbackup' ]] && rm -R /var/urbackup
			[[ -f '/etc/default/urbackupsrv' ]] && rm /etc/default/urbackupsrv
			[[ -f '/etc/logrotate.d/urbackupsrv' ]] && rm /etc/logrotate.d/urbackupsrv
			command -v urbackupsrv > /dev/null && rm "$(which -a urbackupsrv)"
			command -v urbackup_mount_helper > /dev/null && rm "$(which -a urbackup_mount_helper)"
			command -v urbackup_snapshot_helper > /dev/null && rm "$(which -a urbackup_snapshot_helper)"

		fi

		software_id=110
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			umount -f /mnt/nfs_client

			# nfs-kernel-server depends on nfs-common
			(( ${aSOFTWARE_INSTALL_STATE[109]} == 0 )) && G_AGP nfs-common

			# Disable in fstab
			sed -i '/\/mnt\/nfs_client/c\#\/mnt\/nfs_client . Please use DietPi-Drive_Manager to setup this mount' /etc/fstab

			# Add info file for installation method.
			echo -e 'NFS client can be installed and setup by DietPi-Drive_Manager.\nSimply run: dietpi-drive_manager and select the "Add network drive" option.' > /mnt/nfs_client/readme.txt

		fi

		software_id=16
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP build-essential

		fi

		software_id=17 # Git Client
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP git

		fi

		software_id=5 # ALSA
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP alsa-utils
			[[ -d '/etc/systemd/system/alsa-state.service.d' ]] && G_EXEC_NOEXIT=1 G_EXEC rm -R /etc/systemd/system/alsa-state.service.d

		fi

		software_id=6 # Xserver
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			# shellcheck disable=SC2046
			apt-mark auto $(dpkg --get-selections 'x11-*' dbus-x11 'libegl1*' 'libgles2*' libgl1-mesa-dri 'mesa-*' libdrm-rockchip1 libmali-rk-utgard-450-r7p0 'xf86-video-*' malit628-odroid mali450-odroid aml-libs-odroid 'libump*' firmware-samsung 2> /dev/null | mawk '{print $1}')
			G_AGP 'xorg*' 'xserver-xorg*' xinit xcompmgr xterm xfonts-base
			rm -f /etc/xdg/autostart/xcompmgr.desktop /etc/X11/xorg.conf /etc/X11/xorg.conf.d/98-dietpi-disable_dpms.conf

		fi

		software_id=7 # FFmpeg
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP ffmpeg

		fi

		software_id=8 # Java
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			# shellcheck disable=SC2046
			apt-mark auto $(dpkg --get-selections 'default-jre*' 'default-jdk*' 'openjdk-*' ca-certificates-java 2> /dev/null | mawk '{print $1}')

		fi

		software_id=104 # Dropbear
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP 'dropbear*'

		fi

		software_id=105 # OpenSSH Server
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP openssh-{,sftp-}server

		fi

		software_id=181 # PaperMC
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 ))
		then
			Banner_Uninstalling
			# Service
			if [[ -f '/etc/systemd/system/papermc.service' ]]
			then
				systemctl disable --now papermc
				rm -R /etc/systemd/system/papermc.service*
			fi
			[[ -d '/etc/systemd/system/papermc.service.d' ]] && rm -R /etc/systemd/system/papermc.service.d
			# User
			getent passwd papermc > /dev/null && userdel papermc
			getent group papermc > /dev/null && groupdel papermc
			# Files
			[[ -d '/opt/papermc' ]] && rm -R /opt/papermc
			[[ -d '/var/log/papermc' ]] && rm -R /var/log/papermc
			[[ -d '/mnt/dietpi_userdata/papermc' ]] && rm -R /mnt/dietpi_userdata/papermc
			[[ -f '/usr/local/bin/mcrcon' ]] && rm /usr/local/bin/mcrcon
		fi

		software_id=103 # DietPi-RAMlog
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling

			G_EXEC sed -i '/[[:blank:]]\/var\/log[[:blank:]]/d' /etc/fstab

			G_EXEC mkdir -p /var/lib/dietpi/dietpi-ramlog
			cat << '_EOF_' > /var/lib/dietpi/dietpi-ramlog/disable.sh
#!/bin/dash
{
	systemctl disable --now dietpi-ramlog
	rm -R /var/tmp/dietpi/logs/dietpi-ramlog_store
	systemctl disable dietpi-ramlog_disable
	rm /etc/systemd/system/dietpi-ramlog_disable.service
	rm /var/lib/dietpi/dietpi-ramlog/disable.sh
}
_EOF_
			G_EXEC chmod +x /var/lib/dietpi/dietpi-ramlog/disable.sh

			cat << '_EOF_' > /etc/systemd/system/dietpi-ramlog_disable.service
[Unit]
Description=DietPi-RAMlog_disable
After=dietpi-ramlog.service
Before=dietpi-preboot.service rsyslog.service syslog.service

[Service]
Type=oneshot
StandardOutput=tty
ExecStart=/bin/dash -c '/var/lib/dietpi/dietpi-ramlog/disable.sh > /var/tmp/dietpi/logs/dietpi-ramlog_disable_debug.log 2>&1'

[Install]
WantedBy=local-fs.target
_EOF_
			G_EXEC systemctl daemon-reload
			G_EXEC systemctl enable dietpi-ramlog_disable

		fi

		software_id=101 # Logrotate
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP logrotate

		fi

		software_id=102 # Rsyslog
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_DIETPI-NOTIFY 2 'Marking rsyslog for autoremoval, hence it stays installed if any other package depends on it'
			apt-mark auto rsyslog # https://github.com/MichaIng/DietPi/issues/2454

		fi

		software_id=9 # Node.js
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			npm r -g --unsafe-perm n yarn npm

			# Old install via repo
			G_AGP nodejs
			[[ -f '/etc/apt/sources.list.d/nodesource_nodejs.list' ]] && rm /etc/apt/sources.list.d/nodesource_nodejs.list

			[[ -f '/usr/local/bin/node' ]] && rm /usr/local/bin/node
			[[ -d '/usr/local/n' ]] && rm -R /usr/local/n
			[[ -d '/usr/local/yarn' ]] && rm -R /usr/local/yarn
			[[ -d '/usr/local/include/node' ]] && rm -R /usr/local/include/node
			[[ -d '/usr/local/lib/node_modules' ]] && rm -R /usr/local/lib/node_modules
			[[ -d '/usr/local/share/doc/node' ]] && rm -R /usr/local/share/doc/node
			[[ -f '/usr/local/man/man1/node.1' ]] && rm /usr/local/man/man1/node.1
			[[ -f '/usr/local/share/man/man1/node.1' ]] && rm /usr/local/share/man/man1/node.1
			[[ -f '/usr/local/README.md' ]] && rm /usr/local/README.md
			[[ -f '/usr/local/CHANGELOG.md' ]] && rm /usr/local/CHANGELOG.md
			[[ -f '/usr/local/LICENSE' ]] && rm /usr/local/LICENSE
			[[ -f '/usr/local/share/systemtap/tapset/node.stp' ]] && rm /usr/local/share/systemtap/tapset/node.stp
			[[ -d '/root/.npm' ]] && rm -R /root/.npm
			[[ -f '/root/.config/configstore/update-notifier-npm.json' ]] && rm /root/.config/configstore/update-notifier-npm.json

		fi

		software_id=4
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			G_AGP vifm

		fi

		software_id=170 # UnRAR
		if (( aSOFTWARE_INSTALL_STATE[$software_id] == -1 )); then

			Banner_Uninstalling
			G_AGP unrar

		fi

		software_id=130 # Python 3 pip
		if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == -1 )); then

			Banner_Uninstalling
			pip3 uninstall -y pip
			G_AGP python3-pip # Pre-v6.32
			[[ -f '/etc/pip.conf' ]] && rm /etc/pip.conf

		fi

		G_DIETPI-NOTIFY 3 "$G_PROGRAM_NAME" 'Finalise uninstall'

		# Uninstall finished, set all uninstalled software to state 0 (not installed)
		# - Apply same states to Allo and Allo_update
		(( ${aSOFTWARE_INSTALL_STATE[159]} == -1 || ${aSOFTWARE_INSTALL_STATE[160]} == -1 )) && { aSOFTWARE_INSTALL_STATE[159]=0; aSOFTWARE_INSTALL_STATE[160]=0; }
		for i in "${!aSOFTWARE_INSTALL_STATE[@]}"
		do

			(( ${aSOFTWARE_INSTALL_STATE[$i]} == -1 )) && aSOFTWARE_INSTALL_STATE[$i]=0

		done

		# apt-get autoremove
		G_AGA

		# Check if we need to clear DietPi choices
		local fp_temp='.dietpi-uninstall_dpkg'
		dpkg --get-selections | mawk '{print $1}' > $fp_temp
		if ! grep -qE '^(openssh-server|dropbear)' $fp_temp; then

			INDEX_SSHSERVER_CURRENT=0
			INDEX_SSHSERVER_TARGET=0

		fi

		if ! grep -qE '^(samba$|proftpd-basic)' $fp_temp; then

			INDEX_FILESERVER_CURRENT=0
			INDEX_FILESERVER_TARGET=0

		fi

		if ! grep -q '[[:blank:]]/var/log[[:blank:]]' /etc/fstab && ! grep -q '^rsyslog' $fp_temp; then

			INDEX_LOGGING_CURRENT=0
			INDEX_LOGGING_TARGET=0

		fi

		rm $fp_temp

		systemctl daemon-reload

		#----------------------------------------------------------------------
		# Done, reset uninstall flag
		UNINSTALL_REQUIRED=0
		#----------------------------------------------------------------------

	}

	Run_Installations(){

		#------------------------------------------------------------
		# Prevent continue if Network or NTPD is not completed: https://github.com/MichaIng/DietPi/issues/786
		Check_Internet_and_NTPD
		#------------------------------------------------------------
		# Disable powersaving on main screen during installation
		command -v setterm > /dev/null && setterm -blank 0 -powersave off 2> /dev/null
		#------------------------------------------------------------
		# Unmask all services: https://github.com/MichaIng/DietPi/issues/1320
		/boot/dietpi/dietpi-services unmask
		# Stop services
		[[ $G_SERVICE_CONTROL == 0 ]] || /boot/dietpi/dietpi-services stop
		#------------------------------------------------------------
		# Generate userdata folders
		Create_UserContent_Folders
		#------------------------------------------------------------
		# Update package cache
		G_AGUP

		# Full package upgrade on first run installs: https://github.com/MichaIng/DietPi/issues/3098
		(( $G_DIETPI_INSTALL_STAGE == 1 )) && G_AGDUG

		# Disable software installation for automated installs, if user input is required
		Install_Disable_Requires_UserInput

		# Apply DietPi choice systems
		Apply_FileServer_Choices
		Apply_SSHServer_Choices
		Apply_Logging_Choices

		# Apply DietPi preference systems
		Apply_Webserver_Preference

		# Update required software that needs to be installed
		Install_Flag_Prereq_Software

		# Read GLOBAL_PW
		Update_Global_Pw

		# Install Linux Software
		Install_Linux_Software

		# Install DietPi Optimised Software
		/boot/dietpi/dietpi-services stop
		Install_Dietpi_Software

		# Apply Uninstall script, if required by e.g. DietPi choice system
		(( $UNINSTALL_REQUIRED )) && Uninstall_Software

		# Apply DietPi configurations and optimisations
		/boot/dietpi/dietpi-services stop
		Install_Apply_Configs

		# Disable services so DietPi-Services can take control (DietPi will start all services from dietpi-postboot.service)
		/boot/dietpi/dietpi-services dietpi_controlled

		# Apply GPU Memory Splits
		Install_Apply_GPU_Settings

		# No-IP configuration
		(( ${aSOFTWARE_INSTALL_STATE[67]} == 1 )) && G_WHIP_YESNO 'No-IP can be setup and configured by using DietPi-Config. Would you like to complete this now?
\n - Once finished, exit DietPi-Config to finish this install.
\n - More information:\nhttps://dietpi.com/phpbb/viewtopic.php?p=58#p58' && /boot/dietpi/dietpi-config 16 1

		# DietPi-AutoStart choice
		if (( $G_DIETPI_INSTALL_STAGE == 2 )) && ((
			${aSOFTWARE_INSTALL_STATE[23]} == 1 ||
			${aSOFTWARE_INSTALL_STATE[24]} == 1 ||
			${aSOFTWARE_INSTALL_STATE[25]} == 1 ||
			${aSOFTWARE_INSTALL_STATE[26]} == 1 ||
			${aSOFTWARE_INSTALL_STATE[31]} == 1 ||
			${aSOFTWARE_INSTALL_STATE[51]} == 1 ||
			${aSOFTWARE_INSTALL_STATE[108]} == 1 ||
			${aSOFTWARE_INSTALL_STATE[112]} == 1 ||
			${aSOFTWARE_INSTALL_STATE[119]} == 1 ||
			${aSOFTWARE_INSTALL_STATE[155]} == 1 ||
			${aSOFTWARE_INSTALL_STATE[173]} == 1 )); then

			G_WHIP_YESNO 'Would you like to configure the DietPi-AutoStart option?
\nThis will allow you to choose which program loads automatically, after the system has booted up, e.g.:
 - Console\n - Desktop\n - Kodi' && /boot/dietpi/dietpi-autostart

		fi

		# Install finished, set all installed software to state 2 (installed)
		# - Apply same states to Allo and Allo_update
		(( ${aSOFTWARE_INSTALL_STATE[159]} == 1 || ${aSOFTWARE_INSTALL_STATE[160]} == 1 )) && { aSOFTWARE_INSTALL_STATE[159]=2; aSOFTWARE_INSTALL_STATE[160]=2; }
		for i in "${!aSOFTWARE_NAME[@]}"
		do

			(( ${aSOFTWARE_INSTALL_STATE[$i]} == 1 )) && aSOFTWARE_INSTALL_STATE[$i]=2

		done

		# Write to .install File
		Write_InstallFileList

		# DietPi-Automation
		if (( $G_DIETPI_INSTALL_STAGE == 1 )); then

			# Remove fake-hwclock, if real hwclock is available
			# REMOVED: "hwclock" succeeds if an RTC connector is available but no battery attached (or empty), hence we cannot guarantee correct RTC time on boot by only testing "hwclock".
			#hwclock &> /dev/null && G_AGP fake-hwclock

			# x86_64 microcode updates, skip on VM
			if (( $G_HW_ARCH == 10 && $G_HW_MODEL != 20 )); then

				grep -qi 'vendor_id.*intel' /proc/cpuinfo && G_AGI intel-microcode
				grep -qi 'vendor_id.*amd' /proc/cpuinfo && G_AGI amd64-microcode

			# RPi4 EEPROM update: https://github.com/MichaIng/DietPi/issues/3217
			elif (( $G_HW_MODEL == 4 )); then

				/boot/dietpi/func/dietpi-set_hardware rpi-eeprom

			fi

			# Custom 1st run Script (Local file)
			if [[ -f '/boot/Automation_Custom_Script.sh' ]]; then

				G_EXEC cp /boot/Automation_Custom_Script.sh /root/AUTO_CustomScript.sh

			# Custom 1st run Script (Online file)
			elif [[ $AUTOINSTALL_CUSTOMSCRIPTURL != '0' ]]; then

				G_CHECK_URL "$AUTOINSTALL_CUSTOMSCRIPTURL"
				G_EXEC curl -sSfL "$AUTOINSTALL_CUSTOMSCRIPTURL" -o /root/AUTO_CustomScript.sh

			fi

			if [[ -f '/root/AUTO_CustomScript.sh' ]]; then

				local fp_log='/var/tmp/dietpi/logs/dietpi-automation_custom_script.log'

				G_DIETPI-NOTIFY 2 'Running custom script, please wait...'
				G_EXEC chmod +x /root/AUTO_CustomScript.sh
				/root/AUTO_CustomScript.sh 2>&1 | tee $fp_log
				if (( ${PIPESTATUS[0]} )); then

					G_DIETPI-NOTIFY 0 "Custom script: $fp_log"

				else

					G_DIETPI-NOTIFY 1 "Custom script: $fp_log"

				fi

				G_EXEC rm /root/AUTO_CustomScript.sh

			fi

			# Apply AutoStart
			/boot/dietpi/dietpi-autostart "$AUTOINSTALL_AUTOSTARTTARGET"

			# Set Install Stage to Finished
			G_DIETPI_INSTALL_STAGE=2
			echo $G_DIETPI_INSTALL_STAGE > /boot/dietpi/.install_stage

		fi

	}

	#/////////////////////////////////////////////////////////////////////////////////////
	# First Run / Automation functions Vars (eg: on a fresh install)
	#/////////////////////////////////////////////////////////////////////////////////////
	FirstRun_Automation_Init(){

		# Get settings
		AUTOINSTALL_ENABLED=$(sed -n '/^[[:blank:]]*AUTO_SETUP_AUTOMATED=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
		AUTOINSTALL_AUTOSTARTTARGET=$(sed -n '/^[[:blank:]]*AUTO_SETUP_AUTOSTART_TARGET_INDEX=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
		AUTOINSTALL_SSHINDEX=$(sed -n '/^[[:blank:]]*AUTO_SETUP_SSH_SERVER_INDEX=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
		AUTOINSTALL_FILESERVERINDEX=$(sed -n '/^[[:blank:]]*AUTO_SETUP_FILE_SERVER_INDEX=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
		AUTOINSTALL_LOGGINGINDEX=$(sed -n '/^[[:blank:]]*AUTO_SETUP_LOGGING_INDEX=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
		AUTOINSTALL_WEBSERVERINDEX=$(sed -n '/^[[:blank:]]*AUTO_SETUP_WEB_SERVER_INDEX=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
		AUTOINSTALL_CUSTOMSCRIPTURL=$(sed -n '/^[[:blank:]]*AUTO_SETUP_CUSTOM_SCRIPT_EXEC=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
		AUTOINSTALL_TIMESYNCMODE=$(sed -n '/^[[:blank:]]*CONFIG_NTP_MODE=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
		AUTOINSTALL_RESTORE=$(sed -n '/^[[:blank:]]*AUTO_SETUP_BACKUP_RESTORE=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
		# Else set defaults
		[[ $AUTOINSTALL_ENABLED ]] || AUTOINSTALL_ENABLED=0
		[[ $AUTOINSTALL_AUTOSTARTTARGET ]] || AUTOINSTALL_AUTOSTARTTARGET=0
		[[ $AUTOINSTALL_SSHINDEX ]] || AUTOINSTALL_SSHINDEX=0
		[[ $AUTOINSTALL_FILESERVERINDEX ]] || AUTOINSTALL_FILESERVERINDEX=0
		[[ $AUTOINSTALL_LOGGINGINDEX ]] || AUTOINSTALL_LOGGINGINDEX=0
		[[ $AUTOINSTALL_WEBSERVERINDEX ]] || AUTOINSTALL_WEBSERVERINDEX=0
		[[ $AUTOINSTALL_CUSTOMSCRIPTURL ]] || AUTOINSTALL_CUSTOMSCRIPTURL=0
		[[ $AUTOINSTALL_TIMESYNCMODE ]] || AUTOINSTALL_TIMESYNCMODE=2
		[[ $AUTOINSTALL_RESTORE ]] || AUTOINSTALL_RESTORE=0

	}

	FirstRun_Automation_Set(){

		# Automated install
		if (( $AUTOINSTALL_ENABLED > 0 )); then

			G_DIETPI-NOTIFY 3 "$G_PROGRAM_NAME" 'Running automated installation'

			TARGETMENUID=-1 # Skip menu loop
			GOSTARTINSTALL=1 # Set install start flag

			# Find all software entries of AUTO_SETUP_INSTALL_SOFTWARE_ID= in dietpi.txt. Then set to state 1 for installation.
			while read -r software_id
			do
				# Flag for installation
				[[ ${aSOFTWARE_NAME[$software_id]} ]] || continue
				aSOFTWARE_INSTALL_STATE[$software_id]=1
				G_DIETPI-NOTIFY 2 "Automation: ${aSOFTWARE_NAME[$software_id]}. Flagged for installation."

			done <<< "$(grep '^[[:blank:]]*AUTO_SETUP_INSTALL_SOFTWARE_ID=' /boot/dietpi.txt | mawk '{print $1}' | sed 's/[^0-9]*//g')"

		fi

		# Further Automated options. (Applied regardless of AUTOINSTALL_ENABLED)
		INDEX_SSHSERVER_TARGET=$AUTOINSTALL_SSHINDEX
		INDEX_FILESERVER_TARGET=$AUTOINSTALL_FILESERVERINDEX
		INDEX_LOGGING_TARGET=$AUTOINSTALL_LOGGINGINDEX
		INDEX_WEBSERVER_TARGET=$AUTOINSTALL_WEBSERVERINDEX

		# Set time sync mode
		/boot/dietpi/func/dietpi-set_software ntpd-mode $AUTOINSTALL_TIMESYNCMODE

	}

	#/////////////////////////////////////////////////////////////////////////////////////
	# Globals
	#/////////////////////////////////////////////////////////////////////////////////////
	Input_Modes(){

		# Process software and exit
		if [[ $1 == 'install' || $1 == 'reinstall' || $1 == 'uninstall' ]]; then

			G_DIETPI-NOTIFY 3 "$G_PROGRAM_NAME" "Automated $1"

			# Make sure we have at least one entry
			[[ $2 ]] || { G_DIETPI-NOTIFY 1 'Please enter at least one software ID to process'; return 1; }

			# Process software IDs
			local command=$1
			shift
			for i in "$@"
			do
				# Check if input software ID exists, install state was defined
				if disable_error=1 G_CHECK_VALIDINT "$i" 0 && disable_error=1 G_CHECK_VALIDINT "${aSOFTWARE_INSTALL_STATE[$i]}"; then

					if [[ $command == 'uninstall' ]]; then

						if (( ${aSOFTWARE_INSTALL_STATE[$i]} == 2 )); then

							aSOFTWARE_INSTALL_STATE[$i]=-1
							UNINSTALL_REQUIRED=1
							G_DIETPI-NOTIFY 0 "Uninstalling ${aSOFTWARE_NAME[$i]}: ${aSOFTWARE_DESC[$i]}"

						elif (( ${aSOFTWARE_INSTALL_STATE[$i]} != -1 )); then

							G_DIETPI-NOTIFY 2 "$i: ${aSOFTWARE_NAME[$i]} is not currently installed"
							G_DIETPI-NOTIFY 0 "No changes applied for: ${aSOFTWARE_NAME[$i]}"

						fi

					elif [[ $command == 'reinstall' ]]; then

						(( ${aSOFTWARE_INSTALL_STATE[$i]} == 1 )) && continue

						if (( ${aSOFTWARE_INSTALL_STATE[$i]} != 2 )); then

							G_DIETPI-NOTIFY 2 "$i: ${aSOFTWARE_NAME[$i]} is not currently installed"
							G_DIETPI-NOTIFY 2 "Use \"dietpi-software install $i\" to install ${aSOFTWARE_NAME[$i]}."
							G_DIETPI-NOTIFY 0 "No changes applied for: ${aSOFTWARE_NAME[$i]}"

						elif (( ! ${aSOFTWARE_AVAIL_G_HW_ARCH[$i,$G_HW_ARCH]:=1} )); then

							G_DIETPI-NOTIFY 1 "Software title (${aSOFTWARE_NAME[$i]}) is not supported on $G_HW_ARCH_NAME systems."

						elif (( ! ${aSOFTWARE_AVAIL_G_HW_MODEL[$i,$G_HW_MODEL]:=1} )); then

							G_DIETPI-NOTIFY 1 "Software title (${aSOFTWARE_NAME[$i]}) is not supported for $G_HW_MODEL_NAME."

						elif (( ! ${aSOFTWARE_AVAIL_G_DISTRO[$i,$G_DISTRO]:=1} )); then

							G_DIETPI-NOTIFY 1 "Software title (${aSOFTWARE_NAME[$i]}) is not supported on Debian ${G_DISTRO_NAME^}."

						else

							aSOFTWARE_INSTALL_STATE[$i]=1
							GOSTARTINSTALL=1 # Set install start flag
							G_DIETPI-NOTIFY 0 "Reinstalling ${aSOFTWARE_NAME[$i]}: ${aSOFTWARE_DESC[$i]}"

						fi

					elif [[ $command == 'install' ]]; then

						(( ${aSOFTWARE_INSTALL_STATE[$i]} == 1 )) && continue

						if (( ! ${aSOFTWARE_AVAIL_G_HW_ARCH[$i,$G_HW_ARCH]:=1} )); then

							G_DIETPI-NOTIFY 1 "Software title (${aSOFTWARE_NAME[$i]}) is not supported on $G_HW_ARCH_NAME systems."

						elif (( ! ${aSOFTWARE_AVAIL_G_HW_MODEL[$i,$G_HW_MODEL]:=1} )); then

							G_DIETPI-NOTIFY 1 "Software title (${aSOFTWARE_NAME[$i]}) is not supported for $G_HW_MODEL_NAME."

						elif (( ! ${aSOFTWARE_AVAIL_G_DISTRO[$i,$G_DISTRO]:=1} )); then

							G_DIETPI-NOTIFY 1 "Software title (${aSOFTWARE_NAME[$i]}) is not supported on Debian ${G_DISTRO_NAME^}."

						elif (( ${aSOFTWARE_INSTALL_STATE[$i]} != 2 )); then

							aSOFTWARE_INSTALL_STATE[$i]=1
							GOSTARTINSTALL=1 # Set install start flag
							G_DIETPI-NOTIFY 0 "Installing ${aSOFTWARE_NAME[$i]}: ${aSOFTWARE_DESC[$i]}"

						else

							G_DIETPI-NOTIFY 2 "$i: ${aSOFTWARE_NAME[$i]} is already installed"
							G_DIETPI-NOTIFY 2 "Use \"dietpi-software reinstall $i\" to force rerun of installation and configuration steps for ${aSOFTWARE_NAME[$i]}."
							G_DIETPI-NOTIFY 0 "No changes applied for: ${aSOFTWARE_NAME[$i]}"

						fi

					fi

				fi

			done

			# Reinstall, prompt for backup
			if [[ $1 == 'reinstall' ]]; then

				(( $GOSTARTINSTALL )) && G_PROMPT_BACKUP

			# Uninstall | Finish up and clear non-required packages
			elif [[ $1 == 'uninstall' ]]; then

				(( $UNINSTALL_REQUIRED )) || return 0
				Uninstall_Software
				Write_InstallFileList

			fi

		# List software IDs, names and additional info
		elif [[ $1 == 'list' ]]; then

			for i in "${!aSOFTWARE_NAME[@]}"
			do

				local string="\e[0mID $i | "
				(( ${aSOFTWARE_INSTALL_STATE[$i]} == 2 )) && string="\e[32mID $i | "

				string+="=${aSOFTWARE_INSTALL_STATE[$i]} | ${aSOFTWARE_NAME[$i]}:\e[0m \e[90m${aSOFTWARE_DESC[$i]}\e[0m |"

				(( ${aSOFTWARE_REQUIRES_ALSA[$i]:=0} == 1 )) && string+=' +ALSA'
				(( ${aSOFTWARE_REQUIRES_XSERVERXORG[$i]:=0} == 1 )) && string+=' +XSERVER'
				(( ${aSOFTWARE_REQUIRES_DESKTOP[$i]:=0} == 1 )) && string+=' +DESKTOP'
				(( ${aSOFTWARE_REQUIRES_FFMPEG[$i]:=0} == 1 )) && string+=' +FFMPEG'
				(( ${aSOFTWARE_REQUIRES_JAVA_JRE_JDK[$i]:=0} == 1 )) && string+=' +JAVA'
				(( ${aSOFTWARE_REQUIRES_NODEJS[$i]:=0} == 1 )) && string+=' +NODEJS'
				(( ${aSOFTWARE_REQUIRES_BUILDESSENTIAL[$i]:=0} == 1 )) && string+=' +BUILDESSENTIAL'
				(( ${aSOFTWARE_REQUIRES_GIT[$i]:=0} == 1 )) && string+=' +GIT'
				(( ${aSOFTWARE_REQUIRES_WEBSERVER[$i]:=0} == 1 )) && string+=' +WEBSERVER'
				(( ${aSOFTWARE_REQUIRES_PHP[$i]:=0} == 1 )) && string+=' +PHP'
				(( ${aSOFTWARE_REQUIRES_MYSQL[$i]:=0} == 1 )) && string+=' +MARIADB'
				(( ${aSOFTWARE_REQUIRES_SQLITE[$i]:=0} == 1 )) && string+=' +SQLITE'

				# - Available for G_HW_ARCH?
				if (( ! ${aSOFTWARE_AVAIL_G_HW_ARCH[$i,$G_HW_ARCH]:=1} )); then

					string+=" \e[31mDISABLED for $G_HW_ARCH_NAME\e[0m"

				# - Available for G_HW_MODEL?
				elif (( ! ${aSOFTWARE_AVAIL_G_HW_MODEL[$i,$G_HW_MODEL]:=1} )); then

					string+=" \e[31mDISABLED for $G_HW_MODEL_NAME\e[0m"

				# - Available for G_DISTRO?
				elif (( ! ${aSOFTWARE_AVAIL_G_DISTRO[$i,$G_DISTRO]:=1} )); then

					string+=" \e[31mDISABLED for Debian $G_DISTRO_NAME\e[0m"

				fi

				# - Online docs
				[[ ${aSOFTWARE_ONLINEDOC_URL[$i]} ]] && string+=" | \e[90m${aSOFTWARE_ONLINEDOC_URL[$i]}\e[0m"

				# - Convert string to lowercase
				echo -e "${string,,}"

			done

		elif [[ $1 == 'free' ]]; then

			# Get highest software array index
			local max=0
			for max in "${!aSOFTWARE_NAME[@]}"; do :; done

			# Check for unused indices
			local free=
			for (( i=0; i<=$max; i++ )); do	[[ ${aSOFTWARE_NAME[$i]} ]] || free+=" $i"; done

			echo "Free software ID(s):${free:- None, so use $(($max+1))!}"

		else

			G_DIETPI-NOTIFY 1 "Invalid input command ($1). Aborting...\n$USAGE"
			exit 1

		fi

	}

	#/////////////////////////////////////////////////////////////////////////////////////
	# Whip menus
	#/////////////////////////////////////////////////////////////////////////////////////
	MENU_MAIN_LASTITEM='Help!'
	TARGETMENUID=0

	Menu_CreateSoftwareList(){

		# Software type for this menu
		local input_mode=$1 #-1=search all mode #0=dietpi 1=linux
		local display_software_menu=1 #only used to disable display of CHECKLIST when search finds no results

		# Search Mode
		if (( $input_mode == -1 )); then

			local search_string= # show all
			display_software_menu=0 # prevent display of CHECKLIST if no results found
			if G_WHIP_INPUTBOX 'Please enter a software title/index to search (eg: desktop/cloud/media/torrent)'; then

				G_WHIP_CHECKLIST_ARRAY=()
				search_string=$G_WHIP_RETURNED_VALUE
				for i in "${!aSOFTWARE_NAME[@]}"
				do

					if (( ${aSOFTWARE_AVAIL_G_HW_MODEL[$i,$G_HW_MODEL]:=1} && ${aSOFTWARE_AVAIL_G_HW_ARCH[$i,$G_HW_ARCH]:=1} && ${aSOFTWARE_AVAIL_G_DISTRO[$i,$G_DISTRO]:=1} )) &&
						[[ $search_string == "$i" || ${aSOFTWARE_NAME[$i],,} == *"${search_string,,}"* || ${aSOFTWARE_DESC[$i],,} == *"${search_string,,}"* ]]; then

						local selected='off'
						if (( ${aSOFTWARE_INSTALL_STATE[$i]} > 0 )); then

							selected='on'
							(( ${aSOFTWARE_INSTALL_STATE[$i]} == 1 )) && aSOFTWARE_INSTALL_STATE[$i]=0 # Reset to 0. Menu checklists will apply back to 1

						fi

						G_WHIP_CHECKLIST_ARRAY+=("$i" "${aSOFTWARE_NAME[$i]}: ${aSOFTWARE_DESC[$i]}" "$selected")
						display_software_menu=1

					fi

				done

				(( $display_software_menu )) || G_WHIP_MSG "Search was unable to find any items for '$search_string'"

			fi

		# Generate Whiptail menu list, of all items in specific software type, based on category
		else

			# Prewarnings - Linux
			if (( $input_mode == 1 )); then

				# Inform User that DietPi software will automatically install additional linux software when required.
				if (( ! $USER_LINUX_AUTOINSTALL_PROMPT_DISPLAYED )); then

					G_WHIP_MSG 'DietPi will automatically install additional Linux software on the next screen, when required (eg: Desktop LXDE will install ALSA + Xserver).\n\nThis means you only need to select the software you actually require.'
					USER_LINUX_AUTOINSTALL_PROMPT_DISPLAYED=1

				fi

			fi

			G_WHIP_CHECKLIST_ARRAY=()

			local max_categories=${#aSOFTWARE_CATEGORIES_DIETPI[@]}
			(( $input_mode == 1 )) && max_categories=${#aSOFTWARE_CATEGORIES_LINUX[@]}

			for ((i=0; i<$max_categories; i++))
			do

				# Only add the category if we have software for it.
				local category_enabled=0

				# Now run through all available software
				for j in "${!aSOFTWARE_CATEGORY_INDEX[@]}"
				do

					# Check if this software matches the current category and software type for this menu.
					(( ${aSOFTWARE_CATEGORY_INDEX[$j]} == $i && ${aSOFTWARE_TYPE[$j]} == $input_mode )) || continue

					# Is available for hardware, arch and distro?
					(( ${aSOFTWARE_AVAIL_G_HW_MODEL[$j,$G_HW_MODEL]:=1} && ${aSOFTWARE_AVAIL_G_HW_ARCH[$j,$G_HW_ARCH]:=1} && ${aSOFTWARE_AVAIL_G_DISTRO[$j,$G_DISTRO]:=1} )) || continue

					local selected='off'

					if (( ${aSOFTWARE_INSTALL_STATE[$j]} > 0 )); then

						selected='on'
						# Reset pending state to 0. Menu checklists will apply back to 1
						(( ${aSOFTWARE_INSTALL_STATE[$j]} == 1 )) && aSOFTWARE_INSTALL_STATE[$j]=0

					fi

					# Add category
					if (( $category_enabled == 0 )); then

						# dietpi
						if (( $input_mode == 0 )); then

							G_WHIP_CHECKLIST_ARRAY+=('' "${aSOFTWARE_CATEGORIES_DIETPI[$i]}" 'off')

						# linux
						elif (( $input_mode == 1 )); then

							G_WHIP_CHECKLIST_ARRAY+=('' "${aSOFTWARE_CATEGORIES_LINUX[$i]}" 'off')

						fi

						category_enabled=1

					fi

					# Add this option to whiptail list
					G_WHIP_CHECKLIST_ARRAY+=("$j" "${aSOFTWARE_NAME[$j]}: ${aSOFTWARE_DESC[$j]}" "$selected")

				done

			done

		fi

		if (( $display_software_menu )); then

			G_WHIP_SIZE_X_MAX=95 # Assure this is enough to show full software descriptions + scroll bar
			G_WHIP_BUTTON_CANCEL_TEXT='Back'
			G_WHIP_CHECKLIST "Please use the spacebar to select the software you wish to install.\n - Software and usage details: https://dietpi.com/docs/software/
 - NB: Pressing 'ESC' or selecting 'Back' will clear all changed selections"

			# Reset Choices made flag
			INSTALL_SOFTWARE_CHOICESMADE=0

			# Check for matching results (selected items)
			for i in $G_WHIP_RETURNED_VALUE
			do
				# Enable
				(( ${aSOFTWARE_INSTALL_STATE[$i]} == 0 )) || continue
				INSTALL_SOFTWARE_CHOICESMADE=1
				aSOFTWARE_INSTALL_STATE[$i]=1
			done

			#-----------------------------------------------------------------------------
			# Install Info/Warnings

			# DietPi-Drive_Manager can be used to setup Samba/NFS shares with ease!
			if (( ${aSOFTWARE_INSTALL_STATE[1]} == 1 || ${aSOFTWARE_INSTALL_STATE[110]} == 1 )); then

				G_WHIP_MSG "NFS/Samba client info:\n\nDietPi-Drive_Manager is a powerful tool which vastly simplifies the mounting of NFS and Samba shares.
\nOnce $G_PROGRAM_NAME has finished installation, simply run 'dietpi-drive_manager' to setup required network mounts."

			fi

			# PaperMC: Inform user about long install/startup time and possible swap file usage
			if (( ${aSOFTWARE_INSTALL_STATE[181]} == 1 ))
			then
				local swap_info=
				(( $RAM_PHYS < 924 )) && swap_info='\n\nThe server will be started with with minimal required memory usage, but a swap file will be created to assure that no out-of-memory crash can happen.
On servers with less than 1 GiB physical memory, we strongly recommend to move the swap file to an external drive, if your system runs on an SD card, since during normal PaperMC operation this swap space will be heavily used.'
				G_WHIP_MSG "PaperMC will be started during install to allow pre-configuring it's default configuration files. Especially on smaller SBCs, like Raspberry Pi Zero, this can take a long time.
We allow it to take up to 30 minutes, it's process can be followed, please be patient.$swap_info"
			fi

			# mjpg-streamer: Warn about unprotected stream and inform about additional plugins
			(( ${aSOFTWARE_INSTALL_STATE[137]} == 1 )) && G_WHIP_MSG '[ WARN ] The mjpg-streamer camera stream will be accessible unprotected at port 8082 by default.
\nYou can configure a password protection, but this will break embedding the stream into other web interfaces, like OctoPrint.
\nWe hence recommend to not forward port 8082 through your NAT and/or block public access via firewall.
\nIf you require access from outside your local network to a web interface that embeds the camera stream, we recommend to setup a VPN connection for this.
\nRead more about this matter and how to configure mjpg-streamer at our online documentation: https://dietpi.com/docs/software/camera/#mjpg-streamer
\n[ INFO ] mjpg-streamer will not be compiled with all available plugins by default.
\nIf you require other input or output plugins, simply install the required dependencies. Plugins will be compiled automatically if dependencies are met.
\nFor available plugins and their dependencies, watch the info printed during the build and check out the official GitHub repository: https://github.com/jacksonliam/mjpg-streamer'

			# Gogs: Requires OpenSSH for ssh-keygen binary: https://github.com/MichaIng/DietPi/issues/442
			if (( ${aSOFTWARE_INSTALL_STATE[49]} == 1 && $INDEX_SSHSERVER_TARGET != -2 )); then

				if G_WHIP_YESNO "Gogs requires OpenSSH server to function.\n\nIf you continue, OpenSSH will be selected for install on your system. OpenSSH will also replace Dropbear (if currently installed).
\nWould you like to continue with the Gogs installation?"; then

					# - Use SSH target index to ensure Dropbear gets removed if installed.
					INDEX_SSHSERVER_TARGET=-2

				else

					aSOFTWARE_INSTALL_STATE[49]=0

				fi

			fi

			# Webserver stacks
			for i in 75 76 78 79 81 82
			do

				# Please let DietPi install them for you...
				if (( ${aSOFTWARE_INSTALL_STATE[$i]} == 1 )); then

					G_WHIP_MSG 'DietPi will automatically install a webserver stack (based on your Webserver Preference) when any software that requires a webserver is selected for installation (eg: ownCloud, Pi-hole etc).
\nIt is highly recommended that you allow DietPi to do this for you, ensuring compatibility and stability across DietPi installed programs.\n\nPlease only select a webserver stack if you specifically require it, and, no other webserver stack is installed.
\nTLDR: You do NOT need to select a webserver stack for installation with DietPi. Its all automatic.'
					break

				fi

			done

			# RPi Cam Interface - warn user of locking out camera: https://github.com/MichaIng/DietPi/issues/249
			if (( ${aSOFTWARE_INSTALL_STATE[59]} == 1 )); then

				G_WHIP_MSG 'RPi Cam Control Interface will automatically start and activate the camera during boot. This will prevent other programs (like raspistill) from using the camera.
\nYou can free up the camera by selecting "Stop Camera" from the web interface:\n - http://myip/rpicam'

			fi

			# EmonHUB/EmonPi
			if (( ${aSOFTWARE_INSTALL_STATE[99]} == 1 )); then

				# Enter API KEY
				# Grab key from dietpi.txt
				USER_EMONHUB_APIKEY_CURRENT=$(sed -n '/^[[:blank:]]*SOFTWARE_EMONHUB_APIKEY=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)

				while (( $USER_EMONHUB_APIKEY_COMPLETED == 0 ))
				do

					if G_WHIP_INPUTBOX "EmonHUB/EmonPi:\n\nPlease enter the \"Write API KEY\":\n - Visit https://emoncms.org to register an account and login.
 - Select \"Setup\" from the top right of screen, then select \"My Account\"\n - Enter the \"Write API Key\" into the box below."; then

						USER_EMONHUB_APIKEY_CURRENT=$G_WHIP_RETURNED_VALUE

						if G_WHIP_YESNO "The following \"Write API KEY\" will be applied during installation:\n$USER_EMONHUB_APIKEY_CURRENT\n\nIs this key correct?"; then

							# Update dietpi.txt so the value will be applied during installation.
							sed -i "/^[[:blank:]]*SOFTWARE_EMONHUB_APIKEY=/c\SOFTWARE_EMONHUB_APIKEY=$USER_EMONHUB_APIKEY_CURRENT" /boot/dietpi.txt

							USER_EMONHUB_APIKEY_COMPLETED=1

						fi

					fi

				done

			fi

			# Pi-hole
			if (( ${aSOFTWARE_INSTALL_STATE[93]} == 1 ))
			then
				# Add option to use Unbound as upstream DNS server
				if (( ${aSOFTWARE_INSTALL_STATE[182]} == 0 ))
				then
					G_WHIP_YESNO 'Would you like to use Unbound, a tiny recursive DNS server hosted on your device, as your upstream DNS server?
\nThis will increase privacy, because you will not be sending data to Google etc.
\nHowever, the downside is that some websites may load slower the first time you visit them.' && aSOFTWARE_INSTALL_STATE[182]=1
				fi

				# Prompt for static IP
				if G_WHIP_YESNO 'A static IP address is essential for Pi-hole installations. DietPi-Config can be used to quickly setup your static IP address.
\nIf you have already setup your static IP, please ignore this message.\n\nWould you like to setup your static IP address now?'
				then
					G_WHIP_MSG 'DietPi-Config will now be launched. Simply select your Ethernet or Wifi connection from the menu to access the IP address settings.
\nThe "copy current address to STATIC" menu option can be used to quickly setup your static IP. Please ensure you change the mode "DHCP" to "STATIC".
\nOnce completed, select "Apply Save Changes", then exit DietPi-Config to resume setup.'
					/boot/dietpi/dietpi-config 8 1
				fi
			fi

			# WiFi Hotspot Criteria
			if (( ${aSOFTWARE_INSTALL_STATE[60]} == 1 || ${aSOFTWARE_INSTALL_STATE[61]} == 1 )); then

				# Enable WiFi modules
				/boot/dietpi/func/dietpi-set_hardware wifimodules enable

				while :
				do

					local criteria_passed=1
					local output_string='The following criteria must be met, for the installation of WiFi Hotspot to succeed:'

					if ip r | grep -q "eth$(mawk 'NR==1' /run/dietpi/.network)"; then

						output_string+='\n\n - Ethernet online: PASSED'

					else

						criteria_passed=0
						output_string+='\n\n - Ethernet online: FAILED.\nUse dietpi-config to connect and configure ethernet.'

					fi

					if [[ -d /sys/class/net/wlan$(mawk 'NR==2' /run/dietpi/.network) ]]; then

						output_string+='\n\n - WiFi adapter detected: PASSED'

					else

						criteria_passed=0
						output_string+='\n\n - WiFi adapter detected: FAILED.\nPlease connect a WiFi adapter and try again.'

					fi

					# Passed
					if (( $criteria_passed )); then

						output_string+='\n\nPASSED: Criteria met. Good to go.'
						G_WHIP_MSG "$output_string"
						break

					# Failed, retry?
					else

						output_string+='\n\nFAILED: Criteria not met. Would you like to check again?'
						if ! G_WHIP_YESNO "$output_string"; then

							aSOFTWARE_INSTALL_STATE[60]=0
							aSOFTWARE_INSTALL_STATE[61]=0
							G_WHIP_MSG 'WiFi Hotspot criteria was not met. The software will not be installed.'
							break

						fi

					fi

				done

			fi

			# Remot3.it
			if (( ${aSOFTWARE_INSTALL_STATE[68]} == 1 )); then

				G_WHIP_MSG 'Remot3.it requires you to create an online account, and, link it this device.
\nOnce DietPi has completed your software installations, and rebooted, please follow the First Run tutorial link below:\nhttps://dietpi.com/docs/software/remote_desktop/#remot3it'

			fi

			# Let's Encrypt
			if (( ${aSOFTWARE_INSTALL_STATE[92]} == 1 )); then

				G_WHIP_MSG 'The DietPi installation of Certbot supports all offered web servers.\n\nOnce the installation has finished, you can setup your free SSL cert with:
 - DietPi-LetsEncrypt\n\nThis is a easy to use frontend for Certbot and allows integration into DietPi systems.\n\nMore information:\n - https://dietpi.com/docs/software/system_security/#lets-encrypt'

			fi

			# Box86 warning
			if (( ${aSOFTWARE_INSTALL_STATE[156]} == 1 && $G_HW_ARCH == 2 ))
			then
				G_WHIP_MSG 'WARNING: The piece of software you are about to install is meant for the x86 platform.\n\nBox86 will be used to run it on ARM, however there may be performance and compatibility issues.'
			fi

			# dietpi-config can be used to install/configure the following software. Ask user.
			# - No-IP
			if (( ${aSOFTWARE_INSTALL_STATE[67]} == 1 )); then

				if G_WHIP_YESNO 'No-IP can be setup and configured by using DietPi-Config. Would you like to complete this now?\n\n - Once finished, exit DietPi-Config to resume setup.
\n - More information:\nhttps://dietpi.com/docs/software/advanced_networking/#no-ip'; then

					# Write installed states to temp
					Write_InstallFileList temp

					# Launch DietPi-config
					/boot/dietpi/dietpi-config 16 1

					# Read installed states from temp
					Read_InstallFileList temp

				fi

			fi

			# Home Assistant: Inform about long install/build time: https://github.com/MichaIng/DietPi/issues/2897
			if (( ${aSOFTWARE_INSTALL_STATE[157]} == 1 )); then

				G_WHIP_MSG '[ INFO ] Home Assistant: Grab yourself a coffee
\nThe install process of Home Assistant within the virtual environment, especially the Python build, can take more than one hour, especially on slower SBCs like RPi Zero and similar.
\nPlease be patient. Meanwhile you might want to participate the discussion about reducing (re)install times, by skipping Python rebuild or installing everything outside of the pyenv environment:
 - https://github.com/MichaIng/DietPi/issues/2374'

			fi

		fi

	}

	Menu_Main(){

		# Data for storing SSH server index info
		local index_sshserver_text='None'
		if (( $INDEX_SSHSERVER_TARGET == -1 )); then

			index_sshserver_text='Dropbear'

		elif (( $INDEX_SSHSERVER_TARGET == -2 )); then

			index_sshserver_text='OpenSSH'

		fi

		# Data for storing Fileserver index info
		local index_fileserver_text='None'
		if (( $INDEX_FILESERVER_TARGET == -1 )); then

			index_fileserver_text='ProFTPD'

		elif (( $INDEX_FILESERVER_TARGET == -2 )); then

			index_fileserver_text='Samba'

		fi

		# Data for storing Logging index info
		local index_logging_text='None'
		if (( $INDEX_LOGGING_TARGET == -1 )); then

			index_logging_text='DietPi-RAMlog #1'

		elif (( $INDEX_LOGGING_TARGET == -2 )); then

			index_logging_text='DietPi-RAMlog #2'

		elif (( $INDEX_LOGGING_TARGET == -3 )); then

			index_logging_text='Full'

		fi

		# Hold our string that tells the user what software will be removed when using Index based choice systems
		local toberemoved_text=

		# Check status of USB drive
		Check_USB_Drive_Installed

		# Where is userdata stored?
		local user_data_location_current=$(readlink -f /mnt/dietpi_userdata)

		local user_data_location_description=
		if [[ $user_data_location_current == "$FP_DIETPI_DEDICATED_USBDRIVE" ]]; then

			user_data_location_description="USB Drive | $user_data_location_current"

		elif [[ $user_data_location_current == '/mnt/dietpi_userdata' ]]; then

			user_data_location_description="SD/eMMC | $user_data_location_current"

		else

			user_data_location_description="Custom | $user_data_location_current"

		fi

		# Webserver preference system
		local index_webserver_text='Apache2'
		if (( $INDEX_WEBSERVER_TARGET == -1 )); then

			index_webserver_text='Nginx'

		elif (( $INDEX_WEBSERVER_TARGET == -2 )); then

			index_webserver_text='Lighttpd'

		fi

		G_WHIP_MENU_ARRAY=(

			'Help!' ': Links to online guides, docs and information'
			'DietPi-Config' ': Feature-rich configuration tool for your device'
			'' '●─ Select Software '
			'Search' ': Find a software title for installation'
			'Software Optimised' ': Select DietPi optimised software for installation'
			'Software Additional' ': Select additional Linux software for installation'
			'SSH Server' ": [$index_sshserver_text]"
			'File Server' ": [$index_fileserver_text]"
			'Log System' ": [$index_logging_text]"
			'Webserver Preference' ": [$index_webserver_text]"
			'User Data Location' ": [$user_data_location_description]"
			'' '●─ Install or Remove Software '
			'Uninstall' ': Select installed software for removal'
			'Install' ': Go >> Start installation for selected software'

		)

		G_WHIP_DEFAULT_ITEM=$MENU_MAIN_LASTITEM
		G_WHIP_BUTTON_CANCEL_TEXT='Exit'
		G_WHIP_SIZE_X_MAX=80

		if G_WHIP_MENU; then

			MENU_MAIN_LASTITEM=$G_WHIP_RETURNED_VALUE

			case "$G_WHIP_RETURNED_VALUE" in

				'Uninstall')

					Menu_Uninstall_Software

				;;

				'Search')

					Menu_CreateSoftwareList -1

				;;

				'Software Optimised')

					Menu_CreateSoftwareList 0

				;;

				'Software Additional'*)

					Menu_CreateSoftwareList 1

				;;

				'SSH Server')

					G_WHIP_MENU_ARRAY=(

						'None' ': Not required / manual setup'
						'Dropbear' ': Lightweight SSH Server (Recommended)'
						'OpenSSH' ': Feature rich SSH Server with SFTP/SCP support'

					)

					G_WHIP_DEFAULT_ITEM=$index_sshserver_text
					G_WHIP_BUTTON_CANCEL_TEXT='Back'
					if G_WHIP_MENU 'Please select desired SSH server:
\n- None: Selecting this option will uninstall all SSH servers. This reduces system resources and improves performance. Useful for users who do NOT require networked/remote terminal access.
\n- Dropbear (Recommended): Lightweight SSH server, installed by default on DietPi systems.
\n- OpenSSH: A feature rich SSH server with SFTP/SCP support, at the cost of increased resource usage.'; then

						# Assign target index
						if [[ $G_WHIP_RETURNED_VALUE == 'None' ]]; then

							INDEX_SSHSERVER_TARGET=0
							toberemoved_text='Dropbear and OpenSSH Server'

						elif [[ $G_WHIP_RETURNED_VALUE == 'Dropbear' ]]; then

							INDEX_SSHSERVER_TARGET=-1
							toberemoved_text='OpenSSH Server'

						elif [[ $G_WHIP_RETURNED_VALUE == 'OpenSSH' ]]; then

							INDEX_SSHSERVER_TARGET=-2
							toberemoved_text='Dropbear'

						# Reset to current
						else

							INDEX_SSHSERVER_TARGET=$INDEX_SSHSERVER_CURRENT

						fi

					fi

					# Check for changes
					INSTALL_SSHSERVER_CHOICESMADE=0
					if (( $INDEX_SSHSERVER_TARGET != $INDEX_SSHSERVER_CURRENT )); then

						INSTALL_SSHSERVER_CHOICESMADE=1
						G_WHIP_MSG "$G_WHIP_RETURNED_VALUE has been selected:\n- Your choice will be applied when 'Install Go >> Start installation' is selected.\n- $toberemoved_text installations will be automatically uninstalled."

					fi

				;;

				'File Server')

					G_WHIP_MENU_ARRAY=(

						'None' ': Not required / manual setup'
						'ProFTPD' ': Efficient, lightweight fileserver (recommended)'
						'Samba' ': Feature rich fileserver'

					)

					G_WHIP_DEFAULT_ITEM=$index_fileserver_text
					G_WHIP_BUTTON_CANCEL_TEXT='Back'
					if G_WHIP_MENU 'Please select desired fileserver:
\n- None: Select this option if you do NOT require a method of accessing files and folders on this device, over a network.
\n- ProFTPD (Recommended for RPi v1): Allows you to access/share files on this device efficiently with minimal cpu usage. Uses FTP protocol.
\n- Samba (Recommended for RPi v2): Allows you to easily access/share files on this device, at the cost of higher cpu usage.
\nMore info: https://dietpi.com/docs/dietpi_tools/#quick-selections'; then

						# Assign target index
						if [[ $G_WHIP_RETURNED_VALUE == 'None' ]]; then

							INDEX_FILESERVER_TARGET=0
							toberemoved_text='ProFTPD and Samba Server'

						elif [[ $G_WHIP_RETURNED_VALUE == 'ProFTPD' ]]; then

							INDEX_FILESERVER_TARGET=-1
							toberemoved_text='Samba Server'

						elif [[ $G_WHIP_RETURNED_VALUE == 'Samba' ]]; then

							INDEX_FILESERVER_TARGET=-2
							toberemoved_text='ProFTPD'

						# Reset to current
						else

							INDEX_FILESERVER_TARGET=$INDEX_FILESERVER_CURRENT

						fi

					fi

					# Check for changes
					INSTALL_FILESERVER_CHOICESMADE=0
					if (( $INDEX_FILESERVER_TARGET != $INDEX_FILESERVER_CURRENT )); then

						INSTALL_FILESERVER_CHOICESMADE=1
						G_WHIP_MSG "$G_WHIP_RETURNED_VALUE has been selected:\n- Your choice will be applied when 'Install Go >> Start installation' is selected.\n- $toberemoved_text installations will be automatically uninstalled."

					fi

				;;

				'Log System')

					G_WHIP_MENU_ARRAY=(

						'None' ': Not required / manual setup'
						'DietPi-RAMlog #1' ': Hourly clear (recommended)'
						'DietPi-RAMlog #2' ': Hourly save, then clear'
						'Full' ': Logrotate and Rsyslog'

					)

					G_WHIP_DEFAULT_ITEM=$index_logging_text
					G_WHIP_BUTTON_CANCEL_TEXT='Back'
					if G_WHIP_MENU 'Please select desired logging system:
\n- None: Selecting this option will uninstall DietPi-RAMlog, Logrotate, Rsyslog.
\n- DietPi-RAMlog #1 (Max performance): Mounts /var/log to RAM, reducing filesystem IO. Logfiles are cleared every hour. Does NOT save logfiles to disk.
\n- DietPi-RAMlog #2: Same as #1, with the added feature of saving logfile contents to disk (/root/logfile_storage/*), before being cleared.
\n- Full (Reduces performance): Mounts /var/log to DISK, reduces SDcard lifespan. Full logging system with Logrotate and Rsyslog.'; then

						# Assign target index
						if [[ $G_WHIP_RETURNED_VALUE == 'None' ]]; then

							INDEX_LOGGING_TARGET=0
							toberemoved_text='DietPi-RAMlog, Logrotate, Rsyslog'

						elif [[ $G_WHIP_RETURNED_VALUE == 'DietPi-RAMlog #1' ]]; then

							INDEX_LOGGING_TARGET=-1
							toberemoved_text='Logrotate, Rsyslog'

						elif [[ $G_WHIP_RETURNED_VALUE == 'DietPi-RAMlog #2' ]]; then

							INDEX_LOGGING_TARGET=-2
							toberemoved_text='Logrotate, Rsyslog'

						elif [[ $G_WHIP_RETURNED_VALUE == 'Full' ]]; then

							INDEX_LOGGING_TARGET=-3
							toberemoved_text='DietPi-RAMlog'

						# Reset to current
						else

							INDEX_LOGGING_TARGET=$INDEX_LOGGING_CURRENT

						fi

					fi

					# Check for changes
					INSTALL_LOGGING_CHOICESMADE=0
					if (( $INDEX_LOGGING_TARGET != $INDEX_LOGGING_CURRENT )); then

						INSTALL_LOGGING_CHOICESMADE=1
						G_WHIP_MSG "$G_WHIP_RETURNED_VALUE has been selected:\n - Your choice will be applied when 'Install Go >> Start installation' is selected.\n - $toberemoved_text installations will be automatically uninstalled."

					fi

				;;

				'User Data Location')

					# - Vars if we need to move data.
					local move_data_target=$user_data_location_current

					G_WHIP_MENU_ARRAY=(

						'List' ': Select from a list of available drives to move user data.'
						'Custom' ': Manually enter a location location to move user data.'
						'Drive' ': Launch DietPi-Drive_Manager.'

					)

					G_WHIP_BUTTON_CANCEL_TEXT='Back'
					if G_WHIP_MENU 'Choose where to store your user data. User data includes software such as ownCloud data store, BitTorrent downloads etc.
\nMore information on user data in DietPi:\n- https://dietpi.com/docs/dietpi_tools/#quick-selections
\n- DietPi-Drive_Manager: Launch DietPi-Drive_Manager to setup external drives, and, move user data to different locations.'; then

						# DriveMan
						if [[ $G_WHIP_RETURNED_VALUE == 'Drive' ]]; then

							/boot/dietpi/dietpi-drive_manager

						# List
						elif [[ $G_WHIP_RETURNED_VALUE == 'List' ]]; then

							/boot/dietpi/dietpi-drive_manager 1

							local return_value=$(</tmp/dietpi-drive_manager_selmnt)
							if [[ $return_value ]]; then

								[[ $return_value == '/' ]] && return_value='/mnt'

								move_data_target=$return_value
								move_data_target+='/dietpi_userdata'

							fi

						# Manual filepath entry
						elif [[ $G_WHIP_RETURNED_VALUE == 'Custom' ]]; then

							if G_WHIP_INPUTBOX 'Please input a location. Your user data will be stored inside this location.\n - eg: /mnt/MyDrive/MyData'; then

								move_data_target=$G_WHIP_RETURNED_VALUE

							fi

						fi

						# Move data if the new entry has changed
						if [[ $user_data_location_current != "$move_data_target" ]]; then

							# Ask before we begin
							if G_WHIP_YESNO "DietPi will now attempt to transfer your existing user data to the new location:
\n- From: $user_data_location_current\n- To: $move_data_target\n\nWould you like to begin?"; then

								# Move data, setup symlinks
								if /boot/dietpi/func/dietpi-set_userdata "$user_data_location_current" "$move_data_target"; then

									G_WHIP_MSG "User data transfer: Completed\n\nYour user data has been successfully moved:\n\n- From: $user_data_location_current\n- To: $move_data_target"

								else

									G_WHIP_MSG "User data transfer: Failed\n\n$(</var/log/dietpi-move_userdata.log)\n\nNo changes have been applied."

								fi

							fi

						fi

					fi

				;;

				'Webserver Preference')

					G_WHIP_MENU_ARRAY=(

						'Apache2' ': Popular webserver'
						'Nginx' ': Lightweight webserver'
						'Lighttpd' ': Extremely lightweight webserver'

					)

					G_WHIP_DEFAULT_ITEM=$index_webserver_text
					G_WHIP_BUTTON_CANCEL_TEXT='Back'
					if G_WHIP_MENU 'Please select a Webserver preference, more info https://dietpi.com/docs/software/webserver_stack/#stacks-for-web-development:
\n- Apache2: Feature-rich and popular. Recommended for beginners and users who are looking to follow Apache2 based guides.
\n- Nginx: Lightweight alternative to Apache2. Nginx claims faster webserver performance compared to Apache2.
\n- Lighttpd: Extremely lightweight and is generally considered to offer the \"best\" webserver performance for SBCs. Recommended for users who expect low webserver traffic.'; then

						# Assign target index
						if [[ $G_WHIP_RETURNED_VALUE == 'Apache2' ]]; then

							INDEX_WEBSERVER_TARGET=0

						elif [[ $G_WHIP_RETURNED_VALUE == 'Nginx' ]]; then

							INDEX_WEBSERVER_TARGET=-1

						elif [[ $G_WHIP_RETURNED_VALUE == 'Lighttpd' ]]; then

							INDEX_WEBSERVER_TARGET=-2

						# Reset to current
						else

							INDEX_WEBSERVER_TARGET=$INDEX_WEBSERVER_CURRENT

						fi

						# Check for changes
						if (( $INDEX_WEBSERVER_TARGET != $INDEX_WEBSERVER_CURRENT )); then

							# Check for existing and compatible installed stacks before allowing the change
							local incompatible_webserver_preference=0
							local info_currently_installed_webserver='None'

							if dpkg-query -s 'apache2' &> /dev/null; then

								INDEX_WEBSERVER_CURRENT=0
								info_currently_installed_webserver='Apache2'
								(( $INDEX_WEBSERVER_TARGET != 0 )) && incompatible_webserver_preference=1

							elif dpkg-query -s 'nginx' &> /dev/null; then

								INDEX_WEBSERVER_CURRENT=-1
								info_currently_installed_webserver='Nginx'
								(( $INDEX_WEBSERVER_TARGET != -1 )) && incompatible_webserver_preference=1

							elif dpkg-query -s 'lighttpd' &> /dev/null; then

								INDEX_WEBSERVER_CURRENT=-2
								info_currently_installed_webserver='Lighttpd'
								(( $INDEX_WEBSERVER_TARGET != -2 )) && incompatible_webserver_preference=1

							fi

							# Reset preference selection
							if (( $incompatible_webserver_preference == 1 )); then

								INDEX_WEBSERVER_TARGET=$INDEX_WEBSERVER_CURRENT

								# Inform user
								G_WHIP_MSG "Error: Incompatible Webserver Preference:\n\nUnable to change your webserver preference to $G_WHIP_RETURNED_VALUE.
\nThis is due to an existing and incompatible webserver installation on your system ($info_currently_installed_webserver). Please remove all webserver based software (using dietpi-software > uninstall), before trying again."

							# Apply preference selection
							else

								# Inform user
								G_WHIP_MSG "Webserver Preference Changed:\n\n$G_WHIP_RETURNED_VALUE has been selected as your webserver preference.
\nWhen you select any software for install that requires a webserver, DietPi will automatically install your prefered choice ($G_WHIP_RETURNED_VALUE)."
								# NB: INDEX_WEBSERVER_CURRENT=$INDEX_WEBSERVER_TARGET | is applied during installation with func Apply_Webserver_Preference().

							fi

						fi

					fi

				;;

				'DietPi-Config')

					/boot/dietpi/dietpi-config

				;;

				'Help!')

					# Populate help to text file so we can read it back to whiptail, as a scrollbox.
					local string='───────────────────────────────────────────────────────────────
Welcome to DietPi:
───────────────────────────────────────────────────────────────
Use PageUp/Down or Arrow Up/Down to scroll this help screen.
Press ESC, or TAB then ENTER to exit this help screen.\n
Easy to follow, step by step guides for installing DietPi:
https://dietpi.com/docs/getting_started/\n
For a list of all installation options and their details:
https://dietpi.com/docs/dietpi_optimised_software/\n
───────────────────────────────────────────────────────────────
List of installed software and their online documentation URLs:
───────────────────────────────────────────────────────────────\n'

					# Installed software
					for i in "${!aSOFTWARE_INSTALL_STATE[@]}"
					do
						[[ ${aSOFTWARE_INSTALL_STATE[i]} -gt 0 && ${aSOFTWARE_ONLINEDOC_URL[$i]} ]] || continue
						string+="\n${aSOFTWARE_NAME[$i]}: ${aSOFTWARE_DESC[$i]}\n${aSOFTWARE_ONLINEDOC_URL[$i]}\n"
					done

					G_WHIP_SIZE_X_MAX=70
					G_WHIP_MSG "$string"

				;;

				'Install')

					Menu_StartInstall

				;;

			esac

		# Exit/Abort Setup
		else

			Menu_Exit

		fi

	}

	Menu_Exit(){

		TARGETMENUID=0 # Return to Main Menu

		# 1st run install
		if (( $G_DIETPI_INSTALL_STAGE == 1 )); then

			G_WHIP_MSG 'DietPi has not fully been installed.\nThis must be completed prior to using DietPi by selecting:\n - Go Start Install.'

		# Standard exit
		elif (( $G_DIETPI_INSTALL_STAGE == 2 )); then

			G_WHIP_YESNO 'Do you wish to exit DietPi-Software?\n\nAll changes to software selections will be cleared.' || return 0

			Banner_Aborted
			exit 0

		fi

	}

	Menu_ConfirmInstall(){

		# Obtain list of pending software installation:
		local string_output
		for i in "${!aSOFTWARE_INSTALL_STATE[@]}"
		do

			(( ${aSOFTWARE_INSTALL_STATE[$i]} == 1 )) && string_output+="\n - ${aSOFTWARE_NAME[$i]}: ${aSOFTWARE_DESC[$i]}"

		done
		[[ $G_SERVICE_CONTROL == 0 ]] || string_output+='\nNB: Software services will be temporarily controlled (stopped) by DietPi during this process. Please inform connected users, before continuing. SSH and VNC is not affected.'

		# Confirm Software install
		G_WHIP_YESNO "DietPi is now ready to install your software choices: $string_output
\nSoftware details, usernames, passwords etc:\n - https://dietpi.com/software\n\nWould you like to begin?" || return 0

		TARGETMENUID=-1 # Exit menu loop
		GOSTARTINSTALL=1 # Set install start flag

	}

	Menu_StartInstall(){

		# Check if user made/changed software selections
		if (( $INSTALL_SOFTWARE_CHOICESMADE || $INSTALL_SSHSERVER_CHOICESMADE || $INSTALL_FILESERVER_CHOICESMADE || $INSTALL_LOGGING_CHOICESMADE )); then

			# List selections and ask for confirmation
			Menu_ConfirmInstall

		# Allow to finish first run setup without any selections
		elif (( $G_DIETPI_INSTALL_STAGE == 1 )); then

			G_WHIP_YESNO 'DietPi was unable to detect any additional software selections for install.
\nNB: You can use dietpi-software at a later date, to install optimised software from our catalogue as required.
\nDo you wish to continue with DietPi as a pure minimal image?' || return 0

			TARGETMENUID=-1 # Exit menu loop
			GOSTARTINSTALL=1 # Set install start flag

		# After first run setup has finished, abort install without any selections
		elif (( $G_DIETPI_INSTALL_STAGE == 2 )); then

			G_WHIP_MSG 'No changes have been detected. Unable to start installation.'

		fi

	}

	Menu_Uninstall_Software(){

		# Array which will hold all software IDs to be removed.
		G_WHIP_CHECKLIST_ARRAY=()

		# Obtain list of installed software
		local i software_available_for_uninstall=0
		for i in "${!aSOFTWARE_INSTALL_STATE[@]}"
		do

			if (( ${aSOFTWARE_INSTALL_STATE[$i]} == 2 && ${aSOFTWARE_TYPE[$i]:=-2} > -2 )); then

				G_WHIP_CHECKLIST_ARRAY+=("$i" "${aSOFTWARE_NAME[$i]}: ${aSOFTWARE_DESC[$i]}" 'off')
				software_available_for_uninstall=1

			fi

		done

		# No software installed
		if (( ! $software_available_for_uninstall )); then

			G_WHIP_MSG 'No software is currently installed, or, available for removal.'

		# Run menu
		else

			G_WHIP_BUTTON_CANCEL_TEXT='Back'
			if G_WHIP_CHECKLIST 'Use the spacebar to select the software you would like to remove:'; then

				# - Create list for user to review before removal
				local output_string='The following software will be REMOVED from your system:'
				for i in $G_WHIP_RETURNED_VALUE
				do
					output_string+="\n - ${aSOFTWARE_NAME[$i]} (ID=$i): ${aSOFTWARE_DESC[$i]}"
					UNINSTALL_REQUIRED=1
				done

				(( $UNINSTALL_REQUIRED )) || return 0

				G_WHIP_YESNO "$output_string
\nNB: Uninstalling usually PURGES any related userdata and configs. If you only need to repair or update software, please use \"dietpi-software reinstall <ID>\" instead.
\nDo you wish to continue?" || { UNINSTALL_REQUIRED=0; return 0; }

				# Mark for uninstall
				for i in $G_WHIP_RETURNED_VALUE
				do
					aSOFTWARE_INSTALL_STATE[$i]=-1
				done

				# Run uninstall
				Uninstall_Software

				# Save install states
				Write_InstallFileList

				G_WHIP_MSG 'Uninstall completed'

			fi

		fi

	}

	#/////////////////////////////////////////////////////////////////////////////////////
	# Banner Print
	#/////////////////////////////////////////////////////////////////////////////////////

	Banner_Installing(){ G_DIETPI-NOTIFY 3 "$G_PROGRAM_NAME" "Installing ${aSOFTWARE_NAME[$software_id]}: ${aSOFTWARE_DESC[$software_id]}"; }
	Banner_Configuration(){ G_DIETPI-NOTIFY 3 "$G_PROGRAM_NAME" "Configuring ${aSOFTWARE_NAME[$software_id]}: ${aSOFTWARE_DESC[$software_id]}"; }
	Banner_Uninstalling(){ G_DIETPI-NOTIFY 3 "$G_PROGRAM_NAME" "Uninstalling ${aSOFTWARE_NAME[$software_id]}: ${aSOFTWARE_DESC[$software_id]}"; }
	Banner_Aborted(){

		# 1st run abort
		if (( $G_DIETPI_INSTALL_STAGE == 1 )); then

			/boot/dietpi/func/dietpi-banner 0
			G_DIETPI-NOTIFY 1 '\n Installation aborted by user.\n Installation must be completed prior to using DietPi.\n Please run dietpi-software to restart the installation.\n'

		# Standard abort
		else

			/boot/dietpi/func/dietpi-banner 1

		fi

	}

	#/////////////////////////////////////////////////////////////////////////////////////
	# Main Loop
	#/////////////////////////////////////////////////////////////////////////////////////
	#--------------------------------------------------------------------------------------
	# Init software arrays
	Software_Arrays_Init
	#--------------------------------------------------------------------------------------
	# Load .installed file, update vars, if it exists
	Read_InstallFileList
	#--------------------------------------------------------------------------------------
	# CLI input mode
	if [[ $1 ]]; then

		Input_Modes "$@"

	#--------------------------------------------------------------------------------------
	# Standard launch
	else

		# 1st run dietpi-software
		if (( $G_DIETPI_INSTALL_STAGE == 1 )); then

			# Load all automation vars
			FirstRun_Automation_Init

			# Restore DietPi-Backup
			if (( $AUTOINSTALL_RESTORE )); then

				# Reboot only when backup restore succeeded
				restore_succeeded=0

				G_DIETPI-NOTIFY 2 'DietPi-Backup restore selected, scanning and mounting attached drives...'
				i=0
				while read -r line
				do
					# Mount drives to temporary mount points
					mkdir -p "/mnt/dietpi-backup$i" && mount "$line" "/mnt/dietpi-backup$i"
					((i++))

				done < <(lsblk -rnpo NAME,UUID,MOUNTPOINT | mawk '$2 && ! $3 {print $1}')

				G_DIETPI-NOTIFY 2 'Searching all drives for DietPi-Backup instances...'
				mapfile -t alist < <(find /mnt -type f -name '.dietpi-backup_stats')

				# Interactive restore
				if [[ $AUTOINSTALL_RESTORE == 1 ]]; then

					# Do we have any results?
					if [[ ${alist[0]} ]]; then

						# Create List for Whiptail
						G_WHIP_MENU_ARRAY=()
						for i in "${alist[@]}"
						do
							last_backup_date=$(sed -n '/ompleted/s/^.*: //p' "$i" | tail -1) # Date of last backup for this backup
							backup_directory=${i%/.dietpi-backup_stats} # Backup directory (minus the backup file), that we can use for target backup directory.
							G_WHIP_MENU_ARRAY+=("$backup_directory" ": $last_backup_date")
						done

						export G_DIETPI_SERVICES_DISABLE=1
						G_WHIP_MENU 'Please select a previous backup to restore:' && /boot/dietpi/dietpi-backup -1 "$G_WHIP_RETURNED_VALUE" && restore_succeeded=1
						unset -v G_DIETPI_SERVICES_DISABLE

					else

						G_WHIP_MSG 'No previous backups were found in /mnt/*. Install will continue like normal.'

					fi

				# Non-interactive restore
				elif [[ $AUTOINSTALL_RESTORE == 2 ]]; then

					# Do we have any results?
					if [[ ${alist[0]} ]]; then

						# Restore first found backup
						export G_DIETPI_SERVICES_DISABLE=1
						/boot/dietpi/dietpi-backup -1 "${alist[0]%/.dietpi-backup_stats}" && restore_succeeded=1
						unset -v G_DIETPI_SERVICES_DISABLE

					else

						G_DIETPI-NOTIFY 1 'DietPi-Backup auto-restore was selected but no backup has been found in /mnt/*. Install will continue like normal.'

					fi

					# Downgrade dietpi.txt option
					G_CONFIG_INJECT 'AUTO_SETUP_BACKUP_RESTORE=' 'AUTO_SETUP_BACKUP_RESTORE=1' /boot/dietpi.txt

				fi

				# Remove mounted drives and mount points
				findmnt /mnt/dietpi-backup[0-9]* > /dev/null && umount /mnt/dietpi-backup[0-9]*
				[[ -d '/mnt/dietpi-backup0' ]] && rmdir /mnt/dietpi-backup[0-9]*

				# Reboot on successful restore
				if (( $restore_succeeded )); then

					G_DIETPI-NOTIFY 2 'The system will now reboot into the restored system'
					sync # Failsafe
					sleep 3
					reboot

				fi

			fi

			# Global PW
			# - Automation, apply as per dietpi.txt
			if (( $AUTOINSTALL_ENABLED )); then

				Update_Global_Pw
				# Set again to apply for UNIX users as well
				/boot/dietpi/func/dietpi-set_software passwords "$GLOBAL_PW"

			# - Prompt change global password and login passwords for root and dietpi users
			else

				/boot/dietpi/func/dietpi-set_software passwords

			fi

			# Disable serial? Must stay enabled for the following:
			# - XU4: HC1/HC2 fail to boot into kernel without: https://github.com/MichaIng/DietPi/issues/2038#issuecomment-416089875
			# - ROCKPro64: Fails to boot into kernel without
			# - NanoPi NEO Air: Required for end users/debugging/setting up WiFi without automation
			keep_serial0=1
			if grep -q '^[[:blank:]]*CONFIG_SERIAL_CONSOLE_ENABLE=1' /boot/dietpi.txt &&
				(( $G_HW_MODEL != 11 && $G_HW_MODEL != 42 && $G_HW_MODEL != 64 )) &&
				G_WHIP_YESNO 'Serial console is currently enabled, would you like to disable it?\n - Disabling serial console will reduce memory consumption slightly\n - If you are unsure on what serial console is, it is safe to disable it'; then

				/boot/dietpi/func/dietpi-set_hardware serialconsole disable
				keep_serial0=0

			fi

			# RPi: Convert "serial0" to its actual symlink target
			if (( $G_HW_MODEL < 10 )); then

				if [[ $keep_serial0 == 1 && -L '/dev/serial0' ]]; then

					tty=$(readlink -f /dev/serial0); tty=${tty#/dev/}
					if [[ $(</boot/cmdline.txt) == *'serial0'* ]]; then

						# shellcheck disable=2015
						[[ $(</boot/cmdline.txt) == *"$tty"* ]] && sed -i "s/[[:blank:]]*console=${tty}[^\"[:blank:]]*//" /boot/cmdline.txt || sed -i "s/serial0/$tty/" /boot/cmdline.txt

					fi
					if [[ -f '/etc/systemd/system/getty.target.wants/serial-getty@serial0.service' ]]; then

						systemctl disable serial-getty@serial0
						systemctl enable "serial-getty@$tty"

					fi

				else

					/boot/dietpi/func/dietpi-set_hardware serialconsole disable serial0

				fi

			fi
			unset -v keep_serial0 tty

		fi

		# Prevent continue if no network or time sync is not completed: https://github.com/MichaIng/DietPi/issues/786
		Check_Internet_and_NTPD

		# Apply 1st run automation
		(( $G_DIETPI_INSTALL_STAGE == 1 )) && FirstRun_Automation_Set

		# Start DietPi Menu
		until (( $TARGETMENUID < 0 ))
		do
			G_TERM_CLEAR
			Menu_Main
		done

	fi

	#--------------------------------------------------------------------------------------
	# Start DietPi-Software installs
	if (( $GOSTARTINSTALL )); then

		# Check for sufficient free space, 500 MiB should be enough for most software selections
		if ! G_CHECK_FREESPACE / 500; then

			G_DIETPI-NOTIFY 1 'Install aborted due to insufficient free space'
			exit 1

		fi

		# Userdata location verify
		G_CHECK_USERDATA

		# Start installations for software
		Run_Installations

		# Unmask systemd-logind if Kodi is installed, it's set in dietpi.txt or libpam-systemd was installed
		if [[ $(readlink /etc/systemd/system/systemd-logind.service) == '/dev/null' ]] &&
			{ [[ ${aSOFTWARE_INSTALL_STATE[31]} == 1 ]] || grep -q '^[[:blank:]]*AUTO_UNMASK_LOGIND=1' /boot/dietpi.txt || dpkg-query -s 'libpam-systemd' &> /dev/null; }; then

			# dbus is required for systemd-logind to start
			dpkg-query -s dbus &> /dev/null || G_AGI dbus
			G_EXEC systemctl unmask dbus
			G_EXEC systemctl start dbus
			G_EXEC systemctl unmask systemd-logind
			G_EXEC systemctl start systemd-logind

		fi

		# Upload DietPi-Survey Data, if opted in, prompt user choice, if no settings file exists
		# - Skip, if G_SERVICE_CONTROL == 0, which is exported by patch_file (DietPi-Update) which sends survey already
		[[ $G_SERVICE_CONTROL == 0 ]] || /boot/dietpi/dietpi-survey 1

		G_DIETPI-NOTIFY 3 "$G_PROGRAM_NAME" 'Installation completed'

		# Start services (restart to reload configs of possible running services)
		[[ $G_SERVICE_CONTROL == 0 ]] || /boot/dietpi/dietpi-services restart

		# Start installed services, not controlled by DietPi-Services
		if [[ ${aSTART_SERVICES[0]} ]]
		then
			G_DIETPI-NOTIFY 2 'Starting installed services, not controlled by DietPi-Services'
			for i in "${aSTART_SERVICES[@]}"
			do
				G_EXEC_NOHALT=1 G_EXEC systemctl start "$i"
			done
		fi

	fi

}
